Scoped Storage Stories: The Diabolical Details of Downloads
Android 10 is greatly restricting access to external storage via filesystem APIs. Instead, we need to use other APIs to work with content. This is the eleventh and final(?) post in a series where we will explore how to work with those alternatives, now looking at MediaStore
options.
Another wrinkle introduced by Android 10 was MediaStore.Downloads
. This is advertised as being another MediaStore
collection that we can use, akin to the ones for video, pictures, etc.
The basic usage of MediaStore.Downloads
indeed does match what we use for other MediaStore
collections. The Download Wrangler sample app is a bit of a mash-up of some of the previous samples, with a DownloadRepository
class that offers:
-
A
download()
function to download a URL to a requested filename inDownloads
, and -
A
listTitles()
function to queryDownloads
and return the names of all the items found in there
These have both "Q" and "legacy" variants, where the "Q" edition uses MediaStore.Downloads
and older devices use Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.
Those functions are tied to three toolbar buttons in the UI:
-
A "refresh" button that calls
listTitles()
and updates aRecyclerView
with the titles -
A "download" button that downloads a PDF of an appendix from Elements of Kotlin Coroutines that I used in an Android Summit workshop
-
A "all" button that appears on Android 10 devices and allows you to "upgrade" your permissions with
READ_EXTERNAL_STORAGE
(on Android 9 and older, that permission is requested when you first run the app)
The behavior on legacy devices is what you would expect:
-
The list shows the contents of the
Downloads/
directory -
You need
READ_EXTERNAL_STORAGE
to read the contents of that directory andWRITE_EXTERNAL_STORAGE
to save content to that directory
On Android 10, what we would expect is:
-
Without any permissions, the list contains titles of content that you downloaded by means of
MediaStore.Downloads
-
With
READ_EXTERNAL_STORAGE
, the list contains all content inMediaStore.Downloads
, including those from other apps -
Without any permissions, you can save content to
MediaStore.Downloads
The first and the last items in that list do occur. The second does not. You cannot access other apps' downloads via MediaStore.Downloads
, even with READ_EXTERNAL_STORAGE
permission. For that matter, WRITE_EXTERNAL_STORAGE
does not help.
You can see this by running the sample app. It has two product flavors, cunningly named one
and two
. Run one
and download the PDF, then run two
and use the "all" button to request READ_EXTERNAL_STORAGE
. You will not see the PDF downloaded by one
or anything else that might reside in Downloads/
.
This is covered in the documentation:
In particular, if your app wants to access a file within the
MediaStore.Downloads
collection that your app didn't create, you must use the Storage Access Framework.
For apps that are merely downloading content to MediaStore.Downloads
, or accessing their own downloaded content, this is not a problem. It's just something to bear in mind: Android 10's MediaStore
enhancements are not the same across all collections.
That should wrap up this blog post series, though I may write more on this subject in the future if interesting edge cases come to my attention.
The entire series includes:
- The basics of using the Storage Access Framework
- Getting durable access to the selected content
- Working with
DocumentFile
for individual documents - Working with document trees
- Working with
DocumentsContract
- Problems with the SAF API
- A specific problem with
listFiles()
onDocumentFile
- Storing content using
MediaStore
- Reading content from the
MediaStore
- Modifying
MediaStore
content from other apps - Limitations of
MediaStore.Downloads
#Google #Android #Smartphones #OS #News @ndrdnws #ndrdnws #AndroidNews