FileDownloader.java 27 KB

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