kotlin
154 lines · 1 tab
Alex Chen
Jan 2026
1 tab
package com.example.myapp.utils
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.Cursor
import android.net.Uri
import android.os.Environment
import androidx.core.content.getSystemService
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import java.io.File
class DownloadHelper(private val context: Context) {
private val downloadManager = context.getSystemService<DownloadManager>()!!
fun downloadFile(
url: String,
fileName: String,
description: String = "Downloading file",
mimeType: String? = null,
showNotification: Boolean = true
): Long {
val request = DownloadManager.Request(Uri.parse(url))
.setTitle(fileName)
.setDescription(description)
.setNotificationVisibility(
if (showNotification)
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
else
DownloadManager.Request.VISIBILITY_HIDDEN
)
.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
fileName
)
.setAllowedOverMetered(true)
.setAllowedOverRoaming(false)
mimeType?.let { request.setMimeType(it) }
return downloadManager.enqueue(request)
}
fun observeDownload(downloadId: Long): Flow<DownloadStatus> = callbackFlow {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
if (id == downloadId) {
val status = getDownloadStatus(downloadId)
trySend(status)
if (status is DownloadStatus.Completed ||
status is DownloadStatus.Failed) {
close()
}
}
}
}
context.registerReceiver(
receiver,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
)
// Send initial status
trySend(getDownloadStatus(downloadId))
awaitClose {
context.unregisterReceiver(receiver)
}
}
fun getDownloadStatus(downloadId: Long): DownloadStatus {
val query = DownloadManager.Query().setFilterById(downloadId)
val cursor: Cursor = downloadManager.query(query) ?:
return DownloadStatus.Failed("Download not found")
if (!cursor.moveToFirst()) {
cursor.close()
return DownloadStatus.Failed("Download not found")
}
val statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
val status = cursor.getInt(statusIndex)
val result = when (status) {
DownloadManager.STATUS_PENDING ->
DownloadStatus.Pending
DownloadManager.STATUS_RUNNING -> {
val bytesDownloaded = cursor.getLong(
cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
)
val bytesTotal = cursor.getLong(
cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
)
val progress = if (bytesTotal > 0) {
(bytesDownloaded * 100 / bytesTotal).toInt()
} else 0
DownloadStatus.Running(progress, bytesDownloaded, bytesTotal)
}
DownloadManager.STATUS_SUCCESSFUL -> {
val uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
val uri = cursor.getString(uriIndex)
DownloadStatus.Completed(uri)
}
DownloadManager.STATUS_FAILED -> {
val reasonIndex = cursor.getColumnIndex(DownloadManager.COLUMN_REASON)
val reason = cursor.getInt(reasonIndex)
DownloadStatus.Failed(getFailureReason(reason))
}
else -> DownloadStatus.Failed("Unknown status")
}
cursor.close()
return result
}
private fun getFailureReason(reason: Int): String = when (reason) {
DownloadManager.ERROR_CANNOT_RESUME -> "Cannot resume download"
DownloadManager.ERROR_DEVICE_NOT_FOUND -> "No external storage"
DownloadManager.ERROR_FILE_ALREADY_EXISTS -> "File already exists"
DownloadManager.ERROR_FILE_ERROR -> "File error"
DownloadManager.ERROR_HTTP_DATA_ERROR -> "HTTP data error"
DownloadManager.ERROR_INSUFFICIENT_SPACE -> "Insufficient space"
DownloadManager.ERROR_TOO_MANY_REDIRECTS -> "Too many redirects"
DownloadManager.ERROR_UNHANDLED_HTTP_CODE -> "Unhandled HTTP code"
else -> "Unknown error"
}
fun cancelDownload(downloadId: Long) {
downloadManager.remove(downloadId)
}
}
sealed class DownloadStatus {
object Pending : DownloadStatus()
data class Running(
val progress: Int,
val bytesDownloaded: Long,
val bytesTotal: Long
) : DownloadStatus()
data class Completed(val uri: String) : DownloadStatus()
data class Failed(val error: String) : DownloadStatus()
}
1 file · kotlin
Explain with highlit
DownloadManager handles long-running downloads with notification progress. I create DownloadManager.Request with URI and destination. setNotificationVisibility() controls progress notifications. Headers customize HTTP requests. Network type requirements prevent downloads on metered connections. The manager returns a download ID for tracking. BroadcastReceiver listens for ACTIONDOWNLOADCOMPLETE. Queries check download status—pending, running, successful, failed. The system handles retries, resumption, and network changes. Downloads persist across app restarts. DownloadManager simplifies complex download scenarios, offloading work to the system while providing fine-grained control when needed.
Related snips
kotlin
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
kotlin
android
hilt
by Alex Chen
3 tabs
kotlin
package com.example.myapp.ui
import androidx.lifecycle.*
import kotlinx.coroutines.launch
class UserViewModel(
LiveData transformations and mediators
kotlin
android
livedata
by Alex Chen
1 tab
kotlin
package com.example.myapp.utils
import android.os.Build
import android.os.StrictMode
import android.os.Trace
import timber.log.Timber
Performance optimization and profiling
kotlin
android
performance
by Alex Chen
2 tabs
kotlin
package com.example.myapp.widget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
App widgets for home screen
kotlin
android
widget
by Alex Chen
2 tabs
kotlin
package com.example.myapp.data.repository
import com.example.myapp.data.local.PostDao
import com.example.myapp.data.local.PostEntity
import com.example.myapp.data.remote.ApiService
import com.example.myapp.models.Post
Unit testing with JUnit and MockK
kotlin
android
testing
by Alex Chen
2 tabs
kotlin
package com.example.myapp.utils
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.app.PendingIntent
Notification channels and categories
kotlin
android
notifications
by Alex Chen
1 tab
Share this code
Here's the card — post it anywhere.