I have an app that downloads several files using DownloadManager
. Installing the app on the SD card causes the broadcast receiver to receive an intent with DownloadManager.ERROR_FILE_ERROR
in COLUMN_REASON
.
When installed on internal storage, the app functions correctly. Setting the manifest attribute android:installLocation="internalOnly"
does not force the user experiencing the issue to install internally.
The return value of Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
varies depending on the install location.
On internal storage it looks as expected: /storage/emulated/0/Download
On external storage it does not: /storage/<random_looking_series_of_numbers_and_letters>/Download
The intention is to move the files from the downloads directory to the application's files directory. Later those files will be copied from their locations in the files dir to more specific locations as needed, but remain within the files dir. However, when that second copy is attempted, an exception that the source file doesn't exist is thrown.
fun generateDownloadRequest(url: String, destination: String): DownloadManager.Request {
val uri = Uri.parse(url)
val request = DownloadManager.Request(uri)
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
request.setTitle(destination)
request.setDescription("Downloading ${destination.substringAfterLast(":")}.")
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, destination)
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdirs()
return request
}
class DownloadBroadcastReceiver : BroadcastReceiver() {
private var doOnReceived: (Long) -> Unit = {}
fun setDoOnReceived(action: (Long) -> Unit) {
doOnReceived = action
}
override fun onReceive(context: Context?, intent: Intent?) {
val downloadedId = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
downloadedId?.let {
if (it == -1L) return@let
doOnReceived(it)
}
}
}
The filename I was using for the downloads was using colons as a delimiter, but colons aren't valid characters on FAT filesystems. Changing the delimiter to a hyphen solved the problem.