Apps such as photo apps usually just need access to specific directories in
external storage, such as the Pictures
directory. Existing
approaches to accessing external storage aren't designed to easily provide
targeted directory access for these types of apps. For example:
- Requesting
READ_EXTERNAL_STORAGE
orWRITE_EXTERNAL_STORAGE
in your manifest allows access to all public directories on external storage, which might be more access than what your app needs. - Using the Storage Access Framework usually makes your user pick directories via a system UI, which is unnecessary if your app always accesses the same external directory.
Android 7.0 provides a simplified API to access common external storage directories.
Accessing an External Storage Directory
Use the StorageManager
class to get the
appropriate StorageVolume
instance. Then, create
an intent by calling the
StorageVolume.createAccessIntent()
method of that instance.
Use this intent to access external storage directories. To get a list of
all available volumes, including removable media
volumes, use StorageManager.getStorageVolumes()
.
If you have information about a specific file, use
StorageManager.getStorageVolume(File)
to get the
StorageVolume
that contains the file. Call
createAccessIntent()
on this
StorageVolume
to access
the external storage directory for the file.
On secondary volumes, such as external SD cards, pass in null when calling
createAccessIntent()
to request access to the entire
volume, instead of a specific directory.
createAccessIntent()
returns null if you pass in
null to the primary volume, or if you pass in an invalid directory name.
The following code snippet is an example of how to open the
Pictures
directory in the primary shared storage:
StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE); StorageVolume volume = sm.getPrimaryStorageVolume(); Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES); startActivityForResult(intent, request_code);
The system attempts to grant access to the external directory, and if necessary confirms access with the user using a simplified UI:
If the user grants access, the system calls your
onActivityResult()
override
with a result code of RESULT_OK
, and intent data that contains the URI. Use
the provided URI to access directory information, similar to using URIs
returned by the
Storage
Access Framework.
If the user doesn't grant access, the system calls your
onActivityResult()
override
with a result code of RESULT_CANCELED
, and null intent data.
Getting access to a specific external directory also gains access to subdirectories within that directory.
Accessing a Directory on Removable Media
To use Scoped Directory Access to access directories on removable media,
first add a BroadcastReceiver
that listens for the
MEDIA_MOUNTED
notification, for example:
<receiver android:name=".MediaMountedReceiver" android:enabled="true" android:exported="true" > <intent-filter> <action android:name="android.intent.action.MEDIA_MOUNTED" /> <data android:scheme="file" /> </intent-filter> </receiver>
When the user mounts removable media, like an SD card, the system sends a
MEDIA_MOUNTED
notification. This notification
provides a StorageVolume
object in the intent data
that you can use to access directories on the removable media. The following
example accesses the Pictures
directory on removable media:
// BroadcastReceiver has already cached the MEDIA_MOUNTED // notification Intent in mediaMountedIntent StorageVolume volume = (StorageVolume) mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME); volume.createAccessIntent(Environment.DIRECTORY_PICTURES); startActivityForResult(intent, request_code);
Best Practices
Where possible, persist the external directory access URI so you don't have
to repeatedly ask the user for access. Once the user has granted access, call
getContentResolver()
and
with the returned ContentResolver
call
takePersistableUriPermission()
with the directory access URI. The system will
persist the URI and subsequent access requests will return
RESULT_OK
and not show confirmation
UI to the user.
If the user denies access to an external directory, do not immediately request access again. Repeatedly insisting on access results in a poor user experience. If a request is denied by the user, and the app requests access again, the UI displays a Don't ask again checkbox:
If the user selects Don't ask again and denies the request, all future requests for the given directory from your app will be automatically denied, and no request UI will be presented to the user.