The CommonsBlog

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 as ACTION_VIEW or ACTION_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.

Need a speaker at your Android development meetup? Mark Murphy is available, in person in the Boston/Pittsburgh/DC triangle, or by remote anywhere in the world! Contact Mark for details!