Merge branch 'develop' into cancel_transfer_for_deleted_users
[pub/Android/ownCloud.git] / src / com / owncloud / android / files / services / FileDownloader.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 * Copyright (C) 2012-2015 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2,
7 * as published by the Free Software Foundation.
8 *
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.
13 *
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/>.
16 *
17 */
18
19 package com.owncloud.android.files.services;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.AbstractList;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.Vector;
28 import java.util.concurrent.ConcurrentMap;
29
30 import com.owncloud.android.R;
31 import com.owncloud.android.authentication.AccountUtils;
32 import com.owncloud.android.authentication.AuthenticatorActivity;
33 import com.owncloud.android.datamodel.FileDataStorageManager;
34 import com.owncloud.android.datamodel.OCFile;
35
36 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
37 import com.owncloud.android.lib.common.OwnCloudAccount;
38 import com.owncloud.android.lib.common.OwnCloudClient;
39 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
40 import com.owncloud.android.notifications.NotificationBuilderWithProgressBar;
41 import com.owncloud.android.notifications.NotificationDelayer;
42 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
43 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
44 import com.owncloud.android.lib.common.utils.Log_OC;
45 import com.owncloud.android.lib.resources.files.FileUtils;
46 import com.owncloud.android.operations.DownloadFileOperation;
47 import com.owncloud.android.ui.activity.FileActivity;
48 import com.owncloud.android.ui.activity.FileDisplayActivity;
49 import com.owncloud.android.ui.preview.PreviewImageActivity;
50 import com.owncloud.android.ui.preview.PreviewImageFragment;
51 import com.owncloud.android.utils.ErrorMessageAdapter;
52
53 import android.accounts.Account;
54 import android.accounts.AccountsException;
55 import android.app.NotificationManager;
56 import android.app.PendingIntent;
57 import android.app.Service;
58 import android.content.Intent;
59 import android.os.Binder;
60 import android.os.Handler;
61 import android.os.HandlerThread;
62 import android.os.IBinder;
63 import android.os.Looper;
64 import android.os.Message;
65 import android.os.Process;
66 import android.support.v4.app.NotificationCompat;
67 import android.util.Pair;
68
69 public class FileDownloader extends Service implements OnDatatransferProgressListener {
70
71 public static final String EXTRA_ACCOUNT = "ACCOUNT";
72 public static final String EXTRA_FILE = "FILE";
73
74 private static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
75 private static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";
76 public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";
77 public static final String EXTRA_FILE_PATH = "FILE_PATH";
78 public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
79 public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
80 public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
81
82 private static final String TAG = "FileDownloader";
83
84 private Looper mServiceLooper;
85 private ServiceHandler mServiceHandler;
86 private IBinder mBinder;
87 private OwnCloudClient mDownloadClient = null;
88 private Account mCurrentAccount = null;
89 private FileDataStorageManager mStorageManager;
90
91 private IndexedForest<DownloadFileOperation> mPendingDownloads = new IndexedForest<DownloadFileOperation>();
92
93 private DownloadFileOperation mCurrentDownload = null;
94
95 private NotificationManager mNotificationManager;
96 private NotificationCompat.Builder mNotificationBuilder;
97 private int mLastPercent;
98
99
100 public static String getDownloadAddedMessage() {
101 return FileDownloader.class.getName() + DOWNLOAD_ADDED_MESSAGE;
102 }
103
104 public static String getDownloadFinishMessage() {
105 return FileDownloader.class.getName() + DOWNLOAD_FINISH_MESSAGE;
106 }
107
108 /**
109 * Service initialization
110 */
111 @Override
112 public void onCreate() {
113 super.onCreate();
114 Log_OC.d(TAG, "Creating service");
115 mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
116 HandlerThread thread = new HandlerThread("FileDownloaderThread", Process.THREAD_PRIORITY_BACKGROUND);
117 thread.start();
118 mServiceLooper = thread.getLooper();
119 mServiceHandler = new ServiceHandler(mServiceLooper, this);
120 mBinder = new FileDownloaderBinder();
121 }
122
123
124 /**
125 * Service clean up
126 */
127 @Override
128 public void onDestroy() {
129 Log_OC.v(TAG, "Destroying service" );
130 mBinder = null;
131 mServiceHandler = null;
132 mServiceLooper.quit();
133 mServiceLooper = null;
134 mNotificationManager = null;
135 super.onDestroy();
136 }
137
138
139 /**
140 * Entry point to add one or several files to the queue of downloads.
141 *
142 * New downloads are added calling to startService(), resulting in a call to this method.
143 * This ensures the service will keep on working although the caller activity goes away.
144 */
145 @Override
146 public int onStartCommand(Intent intent, int flags, int startId) {
147 Log_OC.d(TAG, "Starting command with id " + startId);
148
149 if ( !intent.hasExtra(EXTRA_ACCOUNT) ||
150 !intent.hasExtra(EXTRA_FILE)
151 ) {
152 Log_OC.e(TAG, "Not enough information provided in intent");
153 return START_NOT_STICKY;
154 } else {
155 final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
156 final OCFile file = intent.getParcelableExtra(EXTRA_FILE);
157
158 /*Log_OC.v(
159 "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
160 "Received request to download file"
161 );*/
162
163 AbstractList<String> requestedDownloads = new Vector<String>();
164 try {
165 DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
166 newDownload.addDatatransferProgressListener(this);
167 newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
168 Pair<String, String> putResult = mPendingDownloads.putIfAbsent(
169 account, file.getRemotePath(), newDownload
170 );
171 String downloadKey = putResult.first;
172 requestedDownloads.add(downloadKey);
173 /*Log_OC.v(
174 "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
175 "Download on " + file.getRemotePath() + " added to queue"
176 );*/
177
178 // Store file on db with state 'downloading'
179 /*
180 TODO - check if helps with UI responsiveness, letting only folders use FileDownloaderBinder to check
181 FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
182 file.setDownloading(true);
183 storageManager.saveFile(file);
184 */
185
186 sendBroadcastNewDownload(newDownload, putResult.second);
187
188 } catch (IllegalArgumentException e) {
189 Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
190 return START_NOT_STICKY;
191 }
192
193 if (requestedDownloads.size() > 0) {
194 Message msg = mServiceHandler.obtainMessage();
195 msg.arg1 = startId;
196 msg.obj = requestedDownloads;
197 mServiceHandler.sendMessage(msg);
198 }
199 //}
200 }
201
202 return START_NOT_STICKY;
203 }
204
205
206 /**
207 * Provides a binder object that clients can use to perform operations on the queue of downloads,
208 * excepting the addition of new files.
209 *
210 * Implemented to perform cancellation, pause and resume of existing downloads.
211 */
212 @Override
213 public IBinder onBind(Intent arg0) {
214 return mBinder;
215 }
216
217
218 /**
219 * Called when ALL the bound clients were onbound.
220 */
221 @Override
222 public boolean onUnbind(Intent intent) {
223 ((FileDownloaderBinder)mBinder).clearListeners();
224 return false; // not accepting rebinding (default behaviour)
225 }
226
227
228 /**
229 * Binder to let client components to perform operations on the queue of downloads.
230 *
231 * It provides by itself the available operations.
232 */
233 public class FileDownloaderBinder extends Binder implements OnDatatransferProgressListener {
234
235 /**
236 * Map of listeners that will be reported about progress of downloads from a {@link FileDownloaderBinder}
237 * instance.
238 */
239 private Map<Long, OnDatatransferProgressListener> mBoundListeners =
240 new HashMap<Long, OnDatatransferProgressListener>();
241
242
243 /**
244 * Cancels a pending or current download of a remote file.
245 *
246 * @param account ownCloud account where the remote file is stored.
247 * @param file A file in the queue of pending downloads
248 */
249 public void cancel(Account account, OCFile file) {
250 /*Log_OC.v(
251 "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
252 "Received request to cancel download of " + file.getRemotePath()
253 );
254 Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
255 "Removing download of " + file.getRemotePath());*/
256 Pair<DownloadFileOperation, String> removeResult =
257 mPendingDownloads.remove(account, file.getRemotePath());
258 DownloadFileOperation download = removeResult.first;
259 if (download != null) {
260 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
261 "Canceling returned download of " + file.getRemotePath());*/
262 download.cancel();
263 } else {
264 if (mCurrentDownload != null && mCurrentAccount != null &&
265 mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
266 account.name.equals(mCurrentAccount.name)) {
267 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
268 "Canceling current sync as descendant: " + mCurrentDownload.getRemotePath());*/
269 mCurrentDownload.cancel();
270 }
271 }
272 }
273
274 /**
275 * Cancels a pending or current upload for an account
276 *
277 * @param account Owncloud accountName where the remote file will be stored.
278 */
279 public void cancel(Account account) {
280 Log_OC.d(TAG, "Account= " + account.name);
281
282 if (mCurrentDownload != null) {
283 Log_OC.d(TAG, "Current Download Account= " + mCurrentDownload.getAccount().name);
284 if (mCurrentDownload.getAccount().name.equals(account.name)) {
285 mCurrentDownload.cancel();
286 }
287 }
288 // Cancel pending downloads
289 ConcurrentMap downloadsAccount = mPendingDownloads.get(account);
290 Iterator<String> it = downloadsAccount.keySet().iterator();
291 Log_OC.d(TAG, "Number of pending downloads= " + downloadsAccount.size());
292 while (it.hasNext()) {
293 String key = it.next();
294 Log_OC.d(TAG, "download CANCELLED " + key);
295 if (key.startsWith(account.name)) {
296 DownloadFileOperation download;
297 synchronized (mPendingDownloads) {
298 download = mPendingDownloads.get(key);
299 if (download != null) {
300 String remotePath = download.getRemotePath();
301 if (mPendingDownloads.contains(account, remotePath)) {
302 mPendingDownloads.remove(account, remotePath);
303 }
304 }
305 }
306 }
307 }
308 }
309
310 public void clearListeners() {
311 mBoundListeners.clear();
312 }
313
314
315 /**
316 * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or
317 * waiting to download.
318 *
319 * If 'file' is a directory, returns 'true' if any of its descendant files is downloading or
320 * waiting to download.
321 *
322 * @param account ownCloud account where the remote file is stored.
323 * @param file A file that could be in the queue of downloads.
324 */
325 public boolean isDownloading(Account account, OCFile file) {
326 if (account == null || file == null) return false;
327 return (mPendingDownloads.contains(account, file.getRemotePath()));
328 }
329
330
331 /**
332 * Adds a listener interested in the progress of the download for a concrete file.
333 *
334 * @param listener Object to notify about progress of transfer.
335 * @param account ownCloud account holding the file of interest.
336 * @param file {@link OCFile} of interest for listener.
337 */
338 public void addDatatransferProgressListener (
339 OnDatatransferProgressListener listener, Account account, OCFile file
340 ) {
341 if (account == null || file == null || listener == null) return;
342 //String targetKey = buildKey(account, file.getRemotePath());
343 mBoundListeners.put(file.getFileId(), listener);
344 }
345
346
347 /**
348 * Removes a listener interested in the progress of the download for a concrete file.
349 *
350 * @param listener Object to notify about progress of transfer.
351 * @param account ownCloud account holding the file of interest.
352 * @param file {@link OCFile} of interest for listener.
353 */
354 public void removeDatatransferProgressListener (
355 OnDatatransferProgressListener listener, Account account, OCFile file
356 ) {
357 if (account == null || file == null || listener == null) return;
358 //String targetKey = buildKey(account, file.getRemotePath());
359 Long fileId = file.getFileId();
360 if (mBoundListeners.get(fileId) == listener) {
361 mBoundListeners.remove(fileId);
362 }
363 }
364
365 @Override
366 public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
367 String fileName) {
368 //String key = buildKey(mCurrentDownload.getAccount(), mCurrentDownload.getFile().getRemotePath());
369 OnDatatransferProgressListener boundListener = mBoundListeners.get(mCurrentDownload.getFile().getFileId());
370 if (boundListener != null) {
371 boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
372 }
373 }
374
375 /**
376 * Review downloads and cancel it if its account doesn't exist
377 */
378 public void reviewDownloads() {
379 if (mCurrentDownload != null &&
380 !AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
381 mCurrentDownload.cancel();
382 }
383 // The rest of downloads are cancelled when they try to start
384 }
385
386 }
387
388
389 /**
390 * Download worker. Performs the pending downloads in the order they were requested.
391 *
392 * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
393 */
394 private static class ServiceHandler extends Handler {
395 // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak
396 FileDownloader mService;
397 public ServiceHandler(Looper looper, FileDownloader service) {
398 super(looper);
399 if (service == null)
400 throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
401 mService = service;
402 }
403
404 @Override
405 public void handleMessage(Message msg) {
406 @SuppressWarnings("unchecked")
407 AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;
408 if (msg.obj != null) {
409 Iterator<String> it = requestedDownloads.iterator();
410 while (it.hasNext()) {
411 String next = it.next();
412 mService.downloadFile(next);
413 }
414 }
415 Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
416 mService.stopSelf(msg.arg1);
417 }
418 }
419
420
421 /**
422 * Core download method: requests a file to download and stores it.
423 *
424 * @param downloadKey Key to access the download to perform, contained in mPendingDownloads
425 */
426 private void downloadFile(String downloadKey) {
427
428 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
429 "Getting download of " + downloadKey);*/
430 mCurrentDownload = mPendingDownloads.get(downloadKey);
431
432 if (mCurrentDownload != null) {
433 // Detect if the account exists
434 if (AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
435 Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() + " exists");
436 notifyDownloadStart(mCurrentDownload);
437
438 RemoteOperationResult downloadResult = null;
439 try {
440 /// prepare client object to send the request to the ownCloud server
441 if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentDownload.getAccount())) {
442 mCurrentAccount = mCurrentDownload.getAccount();
443 mStorageManager = new FileDataStorageManager(
444 mCurrentAccount,
445 getContentResolver()
446 );
447 } // else, reuse storage manager from previous operation
448
449 // always get client from client manager, to get fresh credentials in case of update
450 OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
451 mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
452 getClientFor(ocAccount, this);
453
454
455 /// perform the download
456 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
457 "Executing download of " + mCurrentDownload.getRemotePath());*/
458 downloadResult = mCurrentDownload.execute(mDownloadClient);
459 if (downloadResult.isSuccess()) {
460 saveDownloadedFile();
461 }
462
463 } catch (AccountsException e) {
464 Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
465 downloadResult = new RemoteOperationResult(e);
466 } catch (IOException e) {
467 Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
468 downloadResult = new RemoteOperationResult(e);
469
470 } finally {
471 /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
472 "Removing payload " + mCurrentDownload.getRemotePath());*/
473
474 Pair<DownloadFileOperation, String> removeResult =
475 mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath());
476
477 /// notify result
478 notifyDownloadResult(mCurrentDownload, downloadResult);
479
480 sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
481 }
482 } else {
483 // Cancel the transfer
484 Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() + " doesn't exist");
485 cancelDownloadsForAccount(mCurrentDownload.getAccount());
486
487 }
488 }
489 }
490
491
492 /**
493 * Updates the OC File after a successful download.
494 */
495 private void saveDownloadedFile() {
496 OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
497 long syncDate = System.currentTimeMillis();
498 file.setLastSyncDateForProperties(syncDate);
499 file.setLastSyncDateForData(syncDate);
500 file.setNeedsUpdateThumbnail(true);
501 file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());
502 file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());
503 // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available
504 file.setMimetype(mCurrentDownload.getMimeType());
505 file.setStoragePath(mCurrentDownload.getSavePath());
506 file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
507 file.setRemoteId(mCurrentDownload.getFile().getRemoteId());
508 mStorageManager.saveFile(file);
509 mStorageManager.triggerMediaScan(file.getStoragePath());
510 }
511
512 /**
513 * Update the OC File after a unsuccessful download
514 */
515 private void updateUnsuccessfulDownloadedFile() {
516 OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
517 file.setDownloading(false);
518 mStorageManager.saveFile(file);
519 }
520
521
522 /**
523 * Creates a status notification to show the download progress
524 *
525 * @param download Download operation starting.
526 */
527 private void notifyDownloadStart(DownloadFileOperation download) {
528 /// create status notification with a progress bar
529 mLastPercent = 0;
530 mNotificationBuilder =
531 NotificationBuilderWithProgressBar.newNotificationBuilderWithProgressBar(this);
532 mNotificationBuilder
533 .setSmallIcon(R.drawable.notification_icon)
534 .setTicker(getString(R.string.downloader_download_in_progress_ticker))
535 .setContentTitle(getString(R.string.downloader_download_in_progress_ticker))
536 .setOngoing(true)
537 .setProgress(100, 0, download.getSize() < 0)
538 .setContentText(
539 String.format(getString(R.string.downloader_download_in_progress_content), 0,
540 new File(download.getSavePath()).getName())
541 );
542
543 /// includes a pending intent in the notification showing the details view of the file
544 Intent showDetailsIntent = null;
545 if (PreviewImageFragment.canBePreviewed(download.getFile())) {
546 showDetailsIntent = new Intent(this, PreviewImageActivity.class);
547 } else {
548 showDetailsIntent = new Intent(this, FileDisplayActivity.class);
549 }
550 showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
551 showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount());
552 showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
553
554 mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
555 this, (int) System.currentTimeMillis(), showDetailsIntent, 0
556 ));
557
558 mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotificationBuilder.build());
559 }
560
561
562 /**
563 * Callback method to update the progress bar in the status notification.
564 */
565 @Override
566 public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath)
567 {
568 int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
569 if (percent != mLastPercent) {
570 mNotificationBuilder.setProgress(100, percent, totalToTransfer < 0);
571 String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1);
572 String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);
573 mNotificationBuilder.setContentText(text);
574 mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotificationBuilder.build());
575 }
576 mLastPercent = percent;
577 }
578
579
580 /**
581 * Updates the status notification with the result of a download operation.
582 *
583 * @param downloadResult Result of the download operation.
584 * @param download Finished download operation
585 */
586 private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {
587 mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker);
588 if (!downloadResult.isCancelled()) {
589 int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker :
590 R.string.downloader_download_failed_ticker;
591
592 boolean needsToUpdateCredentials = (
593 downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
594 downloadResult.isIdPRedirection()
595 );
596 tickerId = (needsToUpdateCredentials) ?
597 R.string.downloader_download_failed_credentials_error : tickerId;
598
599 mNotificationBuilder
600 .setTicker(getString(tickerId))
601 .setContentTitle(getString(tickerId))
602 .setAutoCancel(true)
603 .setOngoing(false)
604 .setProgress(0, 0, false);
605
606 if (needsToUpdateCredentials) {
607
608 // let the user update credentials with one click
609 Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
610 updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
611 updateAccountCredentials.putExtra(
612 AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN
613 );
614 updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
615 updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
616 updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
617 mNotificationBuilder
618 .setContentIntent(PendingIntent.getActivity(
619 this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
620
621 } else {
622 // TODO put something smart in showDetailsIntent
623 Intent showDetailsIntent = new Intent();
624 mNotificationBuilder
625 .setContentIntent(PendingIntent.getActivity(
626 this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
627 }
628
629 mNotificationBuilder.setContentText(
630 ErrorMessageAdapter.getErrorCauseMessage(downloadResult, download, getResources())
631 );
632 mNotificationManager.notify(tickerId, mNotificationBuilder.build());
633
634 // Remove success notification
635 if (downloadResult.isSuccess()) {
636 // Sleep 2 seconds, so show the notification before remove it
637 NotificationDelayer.cancelWithDelay(
638 mNotificationManager,
639 R.string.downloader_download_succeeded_ticker,
640 2000);
641 }
642
643 }
644 }
645
646
647 /**
648 * Sends a broadcast when a download finishes in order to the interested activities can update their view
649 *
650 * @param download Finished download operation
651 * @param downloadResult Result of the download operation
652 * @param unlinkedFromRemotePath Path in the downloads tree where the download was unlinked from
653 */
654 private void sendBroadcastDownloadFinished(
655 DownloadFileOperation download,
656 RemoteOperationResult downloadResult,
657 String unlinkedFromRemotePath) {
658 Intent end = new Intent(getDownloadFinishMessage());
659 end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
660 end.putExtra(ACCOUNT_NAME, download.getAccount().name);
661 end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
662 end.putExtra(EXTRA_FILE_PATH, download.getSavePath());
663 if (unlinkedFromRemotePath != null) {
664 end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath);
665 }
666 sendStickyBroadcast(end);
667 }
668
669
670 /**
671 * Sends a broadcast when a new download is added to the queue.
672 *
673 * @param download Added download operation
674 * @param linkedToRemotePath Path in the downloads tree where the download was linked to
675 */
676 private void sendBroadcastNewDownload(DownloadFileOperation download, String linkedToRemotePath) {
677 Intent added = new Intent(getDownloadAddedMessage());
678 added.putExtra(ACCOUNT_NAME, download.getAccount().name);
679 added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
680 added.putExtra(EXTRA_FILE_PATH, download.getSavePath());
681 added.putExtra(EXTRA_LINKED_TO_PATH, linkedToRemotePath);
682 sendStickyBroadcast(added);
683 }
684
685 /**
686 * Remove downloads of an account
687 * @param account
688 */
689 private void cancelDownloadsForAccount(Account account){
690 // Cancel pending downloads
691 ConcurrentMap downloadsAccount = mPendingDownloads.get(account);
692 Iterator<String> it = downloadsAccount.keySet().iterator();
693 Log_OC.d(TAG, "Number of pending downloads= " + downloadsAccount.size());
694 while (it.hasNext()) {
695 String key = it.next();
696 Log_OC.d(TAG, "download CANCELLED " + key);
697 if (key.startsWith(account.name)) {
698 DownloadFileOperation download;
699 synchronized (mPendingDownloads) {
700 download = mPendingDownloads.get(key);
701 if (download != null) {
702 String remotePath = download.getRemotePath();
703 if (mPendingDownloads.contains(account, remotePath)) {
704 mPendingDownloads.remove(account, remotePath);
705 }
706 }
707 }
708 }
709 }
710 }
711
712
713 }