Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AndroidQ DownloadManager.Request.setDestinationUri

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)

like image 519
dafi Avatar asked Jun 07 '19 04:06

dafi


1 Answers

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

like image 120
CommonsWare Avatar answered Sep 17 '22 23:09

CommonsWare