Show Download progress inside activity using DownloadManager

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))


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);

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'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);