94

I'm trying to reproduce the same progress that DownloadManager shows in Notification bar inside my app, but my progress never is published. I'm trying to update it using runOnUiThread(), but for some reason it's not been updated.

my download:

String urlDownload = "https://dl.dropbox.com/s/ex4clsfmiu142dy/test.zip?token_hash=AAGD-XcBL8C3flflkmxjbzdr7_2W_i6CZ_3rM5zQpUCYaw&dl=1";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(urlDownload));

request.setDescription("Testando");
request.setTitle("Download");
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "teste.zip");

final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);

final long downloadId = manager.enqueue(request);

final ProgressBar mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);

new Thread(new Runnable() {

    @Override
    public void run() {

        boolean downloading = true;

        while (downloading) {

            DownloadManager.Query q = new DownloadManager.Query();
            q.setFilterById(downloadId);

            Cursor cursor = manager.query(q);
            cursor.moveToFirst();
            int bytes_downloaded = cursor.getInt(cursor
                    .getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
            int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

            if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
                downloading = false;
            }

            final double dl_progress = (bytes_downloaded / bytes_total) * 100;

            runOnUiThread(new Runnable() {

                @Override
                public void run() {

                    mProgressBar.setProgress((int) dl_progress);

                }
            });

            Log.d(Constants.MAIN_VIEW_ACTIVITY, statusMessage(cursor));
            cursor.close();
        }

    }
}).start();

my statusMessage method:

private String statusMessage(Cursor c) {
    String msg = "???";

    switch (c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
    case DownloadManager.STATUS_FAILED:
        msg = "Download failed!";
        break;

    case DownloadManager.STATUS_PAUSED:
        msg = "Download paused!";
        break;

    case DownloadManager.STATUS_PENDING:
        msg = "Download pending!";
        break;

    case DownloadManager.STATUS_RUNNING:
        msg = "Download in progress!";
        break;

    case DownloadManager.STATUS_SUCCESSFUL:
        msg = "Download complete!";
        break;

    default:
        msg = "Download is nowhere in sight";
        break;
    }

    return (msg);
}

My log is working perfectly, while my download is running says "Download in progress!" and when it is complete "Download complete!", but the same doesn't occurs on my progress, why? I really need some help, other logics to do that are really appreciated

khcpietro
  • 1,459
  • 2
  • 23
  • 42
Victor Laerte
  • 6,446
  • 13
  • 53
  • 102
  • Could it be that your file is simply too small, and that the download completes before the progress is published? What does the download query return in your task? If the task is only executed after some period of time, you may have some other long-running operation on your main thread. – Paul Lammertsma Apr 03 '13 at 19:59
  • I updated the code, can you take a look now? And about the file lenght it's not too small, I can see the download progress on notification bar – Victor Laerte Apr 03 '13 at 21:00
  • Already answered this question [https://stackoverflow.com/a/73546957/19881504](https://stackoverflow.com/a/73546957/19881504) – Quratulain Naqvi Aug 30 '22 at 18:31

5 Answers5

68

You are dividing two integers:

final double dl_progress = (bytes_downloaded / bytes_total) * 100;

As bytes_downloaded is less than bytes_total, (bytes_downloaded / bytes_total) will be 0, and your progress will therefore always be 0.

Change your calculation to

final int dl_progress = (int) ((bytes_downloaded * 100l) / bytes_total);

to obtain the progress in whole (albeit floored) percentiles.

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
  • @AZ_ Thanks for your contribution. I'd suggest you add your own answer with the more elaborate solution. – Paul Lammertsma Mar 04 '14 at 09:47
  • It's ok I don't want to put another answer of already accepted answer because it will be difficult for users. You can opt not to accept my edit :) – AZ_ Mar 04 '14 at 10:19
  • 1
    If your activity ends and you want to cancel the download, you'll get a `division by zero` fatal error. That's why I did it like `final int dl_progress = ( bytes_total > 0 ? (int) ((bytes_downloaded * 100L) / bytes_total) : 0 );` – KaHa6uc Jun 28 '18 at 11:41
19

Paul's answer is correct but with larger downloads you will hit max int pretty quick and start getting a negative progress. I used this to solve the issue:

final int dl_progress = (int) ((bytes_downloaded * 100l) / bytes_total);
JustinMorris
  • 7,259
  • 3
  • 30
  • 36
8

In case if someone needs implementation of download progress retriever from @Victor Laerte's question in Kotlin with RxJava here you go:

DownloadStateRetriever.kt

class DownloadStateRetriever(private val downloadManager: DownloadManager) {

    fun retrieve(id: Long) {
        var downloading = AtomicBoolean(true)

        val disposable = Observable.fromCallable {
            val query = DownloadManager.Query().setFilterById(id)
            val cursor = downloadManager.query(query)

            cursor.moveToFirst()

            val bytesDownloaded = cursor.intValue(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
            val bytesTotal = cursor.intValue(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)

            if (isSuccessful(cursor)) downloading.set(false)
            cursor.close()

            if (bytesTotal == 0) 0.toInt() else ((bytesDownloaded * 100F) / bytesTotal).toInt()
        }
                .subscribeOn(Schedulers.newThread())
                .delay(1, TimeUnit.SECONDS)
                .repeatUntil { !downloading.get() }
                .subscribe {
                    Timber.i("Subscribed to $id. progress: $it")
                }
    }

    private fun isSuccessful(cursor: Cursor) = status(cursor) == DownloadManager.STATUS_SUCCESSFUL

    private fun status(cursor: Cursor) = cursor.intValue(DownloadManager.COLUMN_STATUS)
}

I have added extensions to cursor for more code clarity:

CursorExtensions.kt

import android.database.Cursor

fun Cursor.column(which: String) = this.getColumnIndex(which)
fun Cursor.intValue(which: String): Int = this.getInt(column(which))
fun Cursor.floatValue(which: String): Float = this.getFloat(column(which))
fun Cursor.stringValue(which: String): String = this.getString(column(which))
fun Cursor.doubleValue(which: String): Double = this.getDouble(column(which))

rahimli
  • 1,373
  • 11
  • 25
4

As paul said, you are dividing two integers, with result always <1.

Always cast your number before divide, which calculate and return in float-point.

Don't forget to handle DivByZero.

final int dl_progress = (int) ((double)bytes_downloaded / (double)bytes_total * 100f);
Dennis C
  • 24,511
  • 12
  • 71
  • 99
2

And in case if someone need to implementation download progress with kotlin and flows - i hope this help someone. This is @rahimli version with adding flows instead of RxJava

Retriever

private fun retrieve(id: Long) = flow {
        val downloading = AtomicBoolean(true)

        while (downloading.get()) {
            val query = DownloadManager.Query().setFilterById(id)
            val cursor = downloadManager.query(query)

            cursor.moveToFirst()

            val bytesDownloaded = cursor.intValue(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
            val bytesTotal = cursor.intValue(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)

            if (isSuccessful(cursor)) downloading.set(false)
           
            cursor.close()

            emit(DownloadingState.Downloading(bytesDownloaded, bytesTotal))

            if (downloading.get()) delay(1000)
        }
    }.flowOn(Dispatchers.IO)

Some helpers/extensions

    private fun isSuccessful(cursor: Cursor) = status(cursor) == DownloadManager.STATUS_SUCCESSFUL
    private fun status(cursor: Cursor) = cursor.intValue(DownloadManager.COLUMN_STATUS)
    private fun Cursor.column(which: String) = this.getColumnIndex(which)
    private fun Cursor.intValue(which: String): Int = this.getInt(column(which))

    sealed class DownloadingState {
        data class Downloading(val downloadedBytes: Int, val totalBytes: Int) : DownloadingState() {
            val progress = if (totalBytes == 0) 0 else ((downloadedBytes * 100) / totalBytes)
        }
        object Failure : DownloadingState()
    }
Solo4
  • 39
  • 3