Storage Access Framework (SAF) in Jetpack Compose

Hocine Abdellatif Houari
2 min readFeb 3, 2023

--

Jetpack compose is the new defacto for new android/kmm projects for 2023, for many of these projects the filesystem is a key part of the app, yet all these years it has been tough to get it right. but hopefully not anymore.

With the introduction of scoped storage on android 10 (Api Level 29), Google closed access to device external files (i.e. files that are outside your app-specific/cache directories), and in return presented these three alternative methods, more about them in the official documentation, we simply have

  • MediaStore API
  • Storage Access Framework (SAF)
  • Manage Files Permission

One thing great about SAF compared to the other APIs is that no permissions required in AndroidManifest.xml for it to work, which means less hassle if you are publishing to Google Play. It works with Android Level 19 (Kitkat 4.4) 'til latest as of writing of this article. In fact the API was implemented alongside scoped storage to move apps from having full access to filesystem, of which google says developers abused this access for years for untended use and raised many security risks. All these points mean this API is built to stay. So how does it work?

Here is a simple example on opening a directory, and persisting the given Uri

If you are familiar with the old way of using ACTION_OPEN_DOCUMENT_TREE intent, you know we are missing something. That is right, we are missing flags, we surely persist Uri with Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION, but if we don’t specify these flags to the rememberLauncherForActivityResult() we cannot read/write to that given Uri, so how do we do it? Simply you have to subclass the ActivityResultContracts.OpenDocumentTree class, and override the createIntent() method to specify your prefered set of flags, here is the subclass that I work with called PermissibleOpenDocumentTreeContract

You can see that our new class is flexible in terms of permissions, if you need write permissions, pass true to the constructor, otherwise, all is good, you can read more about FLAG_GRANT_PREFIX_URI_PERMISSION and FLAG_GRANT_PERSISTABLE_URI_PERMISSION here if you want. Now we update our SimpleScreen, change the contract parameter of rememberLauncherForActivityResult(). Here is how it should look like

Note: the OpenDocumentTree Activity result contract is supported from Android Level 21 (Lolipop 5.0), as is the example.

The code for this working app can be found here.

Happy coding.

--

--

Hocine Abdellatif Houari
Hocine Abdellatif Houari

Written by Hocine Abdellatif Houari

A passionate dev, mostly on NodeJS, Rust, Flutter, and Kotlin Multi-Platform. My articles are free for the benefit of the community. hahouari.dev@gmail.com

Responses (1)