SAFFAQ: The Storage Access Framework FAQ
Given the impending demise of file:
Uri
values,
you have two primary means of getting access to documents that your
app wishes to use:
-
Have other apps push
content:
Uri
values to you, via activities in your app with<intent-filter>
elements for common operations, such asACTION_VIEW
orACTION_EDIT
-
You pull
content:
Uri
values into your app – akin to using “file open” or “file save” dialogs – via the Storage Access Framework
With that in mind, here are some fictionally-asked questions about using the Storage Access Framework.
How Do I Ask the User for a Document?
On API Level 19+ devices, create an Intent
for ACTION_OPEN_DOCUMENT
,
with a MIME type indicating what sort of document you want,
and CATEGORY_OPENABLE
as a category. Start
that with startActivityForResult()
. If you get RESULT_OK
in
onActivityResult()
, the Uri
in the delivered Intent
will point
to the document in question. Then, you can use
ContentResolver
, DocumentFile
, and kin to work with the document,
as I outlined in an earlier blog post.
That’s It?
That’s it.
Why Do People Seem All Angst-y About This, Then?
Well, there are issues.
First is the API Level 19+ requirement. Some developers are already
up to a minSdkVersion
of 19 or higher, but plenty of others are not.
The fallback mechanism is ACTION_GET_CONTENT
and a MIME type.
Google, in its infinite wisdom, thinks that the UI presented by the Storage Access Framework should be optimized for things like Google Drive. As a result, the user has to know to do a bunch of clicks to actually get to where their files are (what we call external storage, what the user calls internal storage).
If you need long-term access to the document, beyond just your current
process, you need to use takePersistableUriPermission()
to try to
get that long-term access. However, you may not get it, because it
may not be offered. At the same time, though, you may lose access to the
document for other reasons (e.g., the user deleted it), so this should
not be a huge impediment.
What If I Want a Wide Range of Possible MIME Types?
Add EXTRA_MIME_TYPES
on the Intent
, as a String[]
of concrete
or wildcard MIME types.
What If I Want a Bunch of Documents at Once?
Add EXTRA_ALLOW_MULTIPLE
to the ACTION_OPEN_DOCUMENT
Intent
,
with a value of true
. Now, instead of getting a single Uri
back,
you may get back several Uri
values. You access these through the
ClipData
object you get by calling getClipData()
on the Intent
delivered to onActivityResult()
. getItemCount()
on the ClipData
tells you how many documents the user opened, and getItemAt(i).getUri()
(for a value of i
) gives you the Uri
for that position i
.
What If I Want to Create a Document?
Use ACTION_CREATE_DOCUMENT
instead of ACTION_OPEN_DOCUMENT
, and make
sure that you use a concrete MIME type (e.g., text/plain
, not text/*
).
Otherwise, it works the same as with ACTION_OPEN_DOCUMENT
: you get a
Uri
back representing the newly-created document, and you can use
ContentResolver
to get an OutputStream
onto which you can write your
data.
What If I Want to Create a Bunch of Documents?
If the “bunch of documents” can (or should) reside in one directory,
use ACTION_OPEN_DOCUMENT_TREE
on API Level 21+ devices. This amounts
to a “directory picker”. You get a Uri
back representing a tree
of documents, and you can use methods on DocumentFile
to create
new documents in that directory.
What If I Want to Create a Bunch of Documents on Older Devices?
Ummm… call ACTION_CREATE_DOCUMENT
several times, on API Level 19+
devices.
What If I Want to Create a Bunch of Documents on Seriously Old Devices?
There is no great way that I can think of to accomplish this through
standard Intents
and such.
How Do I Delete a Document?
Call delete()
on a DocumentFile
for the document. That, in turn,
calls the static deleteDocument()
method on DocumentsContract
.
How Do I Delete a Directory?
Ummm… try delete()
on the DocumentFile
. I do not know if this
is supported, but it is worth a shot.
Why Do We Have to BLEEEEEEEEEEEEEEEP With All This?
Partly, the reason is security. Privacy advocates have been complaining for years about how apps can run around and mess with everything on external storage, causing various issues. For example, apps with read access to external storage can examine photos for geotags; coupled with the photo’s timestamp, you now know where the user was at the time that the photo was taken. Hence, over the years, Google has been progressively tightening down Android with respect to file access.
Partly, the reason is that the documents the user wants may not necessarily be on external storage, and so developers who fixate on local files will harm the broader Android ecosystem.
For example, if Android on Chrome OS continues to expand, external storage for ARC apps is not the Chromebook’s hard drive. Rather, it is more like the external storage for an emulator, accessible only by apps and through development tools. Worse, the current ARC environment has each app walled off in its own AVD-style container, so one app cannot access another app’s external storage. Presumably, the vision is for apps to be using the Storage Access Framework, where the ARC environment can bridge between the Android app and the Chrome OS file areas that Chromebook users are used to working with.
Similarly, if we start seeing Android more in desktop-style settings,
desktops in an organization often rely upon a file server, such as a
Windows SMB server. Android can offer access to that via the Storage
Access Framework, as an SMB server is not significantly different than
is Google Drive from the standpoint of Android. However, once again, this
requires developers to start thinking in terms of documents identified
by content:
Uri
values, more so than thinking purely about local
files.