Skip to content

Instantly share code, notes, and snippets.

@androidovshchik
Created April 3, 2022 06:41
Show Gist options
  • Save androidovshchik/13630a6fc43cce61726dd7c950dac6c7 to your computer and use it in GitHub Desktop.
Save androidovshchik/13630a6fc43cce61726dd7c950dac6c7 to your computer and use it in GitHub Desktop.
package com.girlapp
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.ContentObserver
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.util.ArrayMap
import android.webkit.MimeTypeMap
import org.jetbrains.anko.downloadManager
import java.util.*
import java.util.concurrent.Executors
class File(
var fileKey: String,
var fileName: String,
var fileExtension: String,
var url: String
) {
override fun toString(): String {
return "File(fileKey='$fileKey', fileName='$fileName')"
}
}
@Suppress("MemberVisibilityCanBePrivate")
class DownloadFileManager(context: Context) : ContentObserver(null) {
private val executor = Executors.newSingleThreadExecutor()
private val handler = Handler(Looper.getMainLooper())
private val downloadManager = context.downloadManager
private val contentResolver = context.contentResolver
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
queryDownloads()
}
}
private val listeners = mutableSetOf<Listener>()
private val downloadFiles = Collections.synchronizedMap(ArrayMap<Long, File>())
val isDownloading: Boolean
get() = downloadFiles.isNotEmpty()
init {
// [android.provider.Downloads.Impl.CONTENT_URI]
val uri = Uri.parse("content://downloads/my_downloads")
contentResolver.registerContentObserver(uri, true, this)
context.registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
fun addListener(listener: Listener) {
listeners.add(listener)
}
@Suppress("DEPRECATION")
fun startDownload(file: File) {
val mimeType = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(file.fileExtension)
val request = DownloadManager.Request(Uri.parse(file.url)).apply {
setMimeType(mimeType)
setTitle(file.fileName)
setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, file.fileName)
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
setVisibleInDownloadsUi(true)
allowScanningByMediaScanner()
}
}
val id = downloadManager.enqueue(request)
downloadFiles[id] = file
}
override fun onChange(selfChange: Boolean) {
queryDownloads()
}
private fun queryDownloads() {
executor.execute {
val ids = HashSet(downloadFiles.keys)
val query = DownloadManager.Query().apply {
setFilterById(*ids.toLongArray())
}
downloadManager.query(query).use { cursor ->
while (cursor.moveToNext()) {
val id = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID))
ids.remove(id)
val file = downloadFiles[id] ?: continue
when (cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))) {
DownloadManager.STATUS_SUCCESSFUL -> {
downloadFiles.remove(id)
handler.post {
listeners.forEach {
it.onComplete(file)
}
}
}
DownloadManager.STATUS_FAILED -> {
downloadFiles.remove(id)
handler.post {
listeners.forEach {
it.onFailure(file)
}
}
}
else -> {
val bytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
handler.post {
listeners.forEach {
it.onProgress(file, bytes)
}
}
}
}
}
ids.forEach { id ->
val file = downloadFiles[id]
downloadFiles.remove(id)
if (file != null) {
handler.post {
listeners.forEach {
it.onFailure(file)
}
}
}
}
}
}
}
fun removeListener(listener: Listener) {
listeners.remove(listener)
}
fun release(context: Context) {
context.unregisterReceiver(receiver)
contentResolver.unregisterContentObserver(this)
executor.shutdown()
listeners.clear()
}
interface Listener {
fun onProgress(file: File, downloadBytes: Int)
fun onComplete(file: File)
fun onFailure(file: File)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment