FileDownloader.java 27 KB

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