Downloading Files in Android with DownloadManager
It's quite a common scenario to allow attachments or files to be downloaded onto the user's device in an Android app. These can be attachments in a chat or any other file that you want your users to save to their devices.
The DownloadManager
, a class provided by the Android OS since version 2, is the most convenient solution to implement this functionality. Even though the API is quite straightforward, the necessary permissions are not. To use the DownloadManager
up until Android 10, you had to request WRITE_EXTERNAL_STORAGE
permission. And how you are requesting permissions has changed over time. You used to just declare them in the AndroidManifest
on-install time, then request them on run time. After Android 10, you don't even need to request that permission if you are downloading in a public download folder, such as the Downloads
folder. So, what should have been a very straightforward API call, has become a quite complicated if-then-else scenario.
In this post, I will attempt to provide you with a complete working code for using the DownloadManager
to download a file from the web and save it into the Downloads
folder.
The AndroidManifest
As mentioned above, after Android 10 you don't need to ask for any permission to save a file in the Downloads folder. So there's no need to add the WRITE_EXTERNAL_STORAGE
permission in the AndroidManifest. Thankfully, there's a way to declare permission only for specific versions of Android to not overdeclare permissions when not needed.
The code
private fun downloadFile(url: String) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && // 1.
ContextCompat.checkSelfPermission( // 2.
requireActivity(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
!= PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions( // 3.
requireActivity(),
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1
)
Toast.makeText( // 4,
requireContext(),
"Permission denied. Grant permissions and try again.",
Toast.LENGTH_LONG
).show()
return
}
val request = DownloadManager.Request(Uri.parse(url)) // 5.
.setNotificationVisibility( // 6.
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir( // 7.
Environment.DIRECTORY_DOWNLOADS, "filename_to_save")
downloadManager.enqueue(request) // 8.
Toast.makeText( // 9.
context, "Download started", Toast.LENGTH_SHORT
).show()
}
- We need to request permissions only if the device is running Android 10 and below. A reminder that this applies only for saving to public folders, such as Downloads.
- Use this
ContextCompat
method to check if the necessary permissions are already granted. - This
ActivityCompat
method allows you to request the necessary permissions without worrying about how the permissions are requested for each Android version. - Notify the user that the download cannot proceed due to missing permissions and ask them to try again after granting the permissions.
- The
DownloadManager
request builder for constructing the request. Obviously, the URL is a required parameter. - Define how to communicate to the user about the download and its progress. Here we choose to show the progress of the download in the notification area and show a download completed notification when it's done. Check the documentation for additional options.
- Define where the file should be downloaded. The public Downloads folder required no permissions in Android 10+.
- Add the request to the
DownloadManger
's queue to be executed when possible. - Notify the user that the request has been added to the queue, and the download should start.
That's it! You can now allow the users of your app to download any file from the internet.
Happy coding!