My download code stores images that must 'survive' to app uninstall, I used getExternalStoragePublicDirectory
, but now on Android Q this API is deprecated so I modified my download code
val uri = Uri.fromFile(File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "mysubdir"))
val request = DownloadManager.Request(Uri.parse(url))
.setDestinationUri(uri)
to
val uri = buildImageUri(context.contentResolver, fileName, fileName)
val request = DownloadManager.Request(Uri.parse(url))
.setDestinationUri(uri)
////// Use MediaStore
fun buildImageUri(
cr: ContentResolver,
title: String,
description: String): Uri? {
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, title)
values.put(MediaStore.Images.Media.DISPLAY_NAME, title)
values.put(MediaStore.Images.Media.DESCRIPTION, description)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
val buildTime = System.currentTimeMillis() / 1000
values.put(MediaStore.Images.Media.DATE_ADDED, buildTime)
values.put(MediaStore.Images.Media.DATE_MODIFIED, buildTime)
return cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
}
But I obtain java.lang.IllegalArgumentException: Not a file URI: null
so how the code must be written to be Android Q compliant and without using esoteric tricks like search colon in uri and split string?
The documentation for setDestinationUri is not updated and says
For applications targeting Build.VERSION_CODES.Q or above, omissimis text or a path within the top-level Downloads directory (as returned by Environment#getExternalStoragePublicDirectory(String) with Environment#DIRECTORY_DOWNLOADS)
DownloadManager
still has its long-standing limitation of only supporting file:
schemes, not content:
. I filed an issue to try to get this corrected someday.
Your options are:
Use setDestinationInExternalFilesDir()
on DownloadManager.Request
, which maps to getExternalFilesDir()
, which your app has full access to. If the content is something that should survive an app uninstall, you would need to use this as a temporary location and move the content to another, more durable location yourself.
Use setDestinationInExternalPublicDir()
on DownloadManager.Request
. While the corresponding location (Environment.getExternalStoragePublicDirectory()
) is deprecated, that does not prevent you from using DownloadManager
for it. What may be a problem is that you will not be able to read or write the downloaded file, as you have no access to this directory, once your app is adopting Android Q's scoped storage. So, this is suitable only for "blind" downloads, where your app is downloading something at user request but only the user (not the app) needs access to the downloaded content.
Use setDestinationUri()
on DownloadManager.Request
for some file:
Uri
that represents some other useful location. For example, it is possible (though a bit odd) to want to download to getExternalCacheDir()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With