FileDownloader.java 29 KB

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