FileDownloader.java 27 KB

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