Scoped Storage Stories: DocumentFile

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 third post in a series where we will explore how to work with those alternatives, starting with the Storage Access Framework (SAF).

Whether you use ACTION_OPEN_DOCUMENT or ACTION_CREATE_DOCUMENT, you wind up with a Uri that points to the “document”. Your primary use of that Uri is with ContentResolver and its openInputStream() and openOutputStream() methods, so you can read and/or write the document. This is not that different from having a File and using the FileInputStream and FileOutputStream constructors to be able to read and/or write the document.

However, File has a lot of other useful methods, such as:

  • getName() to get the filename

  • exists() to confirm that an actual file exists at the path wrapped by the File

  • delete() to delete the file

  • and so on

The way to get similar functionality for your document Uri is via DocumentFile. DocumentFile can wrap a File or a document Uri and give you an API that resembles a subset of File functionality, including the three methods that I listed above.

If you have a document Uri, you can wrap it in a DocumentFile via DocumentFile.fromSingleUri(). You can then call getName(), exists(), delete(), and other methods, such as:

  • getType() to get the MIME type associated with the content

  • length() to get the length of the content (i.e., how many bytes you could read in from the InputStream)

  • renameTo() to rename the content

  • and so on

However, there are some limitations:

  • The “name” for a piece of content is not necessarily a filename with an extension. It is a “display name”, and it should be something that the user might recognize. However, if you are expecting SomethingLike-This.png, you may be disappointed. The display name might be a classic filename, but it does not have to be.

  • canWrite() is somewhat broken, in that it may return true for a Uri that you cannot write to

Note that you can also get a DocumentFile for a File via DocumentFile.fromFile(). This allows you to treat document Uri values and files the same, so that portions of your app can be written independently of where the data comes from.

The entire series of “Scoped Storage Stories” posts includes posts on:

Interested in Jetpack Compose? has a weekly newsletter of the latest articles, samples, and other details of Compose development!