page.title=Scoped Directory Access page.keywords=preview,sdk,scoped directory access page.tags=androidn @jd:body <div id="qv-wrapper"> <div id="qv"> <h2>In this document</h2> <ol> <li><a href="#accessing">Accessing an External Storage Directory</a></li> <li><a href="#removable">Accessing a Directory on Removable Media</a></li> <li><a href="#best">Best Practices</a></li> </ol> </div> </div> <p>Apps such as photo apps usually just need access to specific directories in external storage, such as the <code>Pictures</code> directory. Existing approaches to accessing external storage aren't designed to easily provide targeted directory access for these types of apps. For example:</p> <ul> <li>Requesting {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} or {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} in your manifest allows access to all public directories on external storage, which might be more access than what your app needs.</li> <li>Using the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a> usually makes your user pick directories via a system UI, which is unnecessary if your app always accesses the same external directory.</li> </ul> <p>Android N provides a new simplified API to access common external storage directories. </p> <h2 id="accessing">Accessing an External Storage Directory</h2> <p>Use the <code>StorageManager</code> class to get the appropriate <code>StorageVolume</code> instance. Then, create an intent by calling the <code>StorageVolume.createAccessIntent()</code> 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 <code>StorageManager.getVolumesList()</code>.</p> <p>If you have information about a specific file, use <code>StorageManager.getStorageVolume(File)</code> to get the <code>StorageVolume</code> that contains the file. Call <code>createAccessIntent()</code> on this <code>StorageVolume</code> to access the external storage directory for the file.</p> <p> On secondary volumes, such as external SD cards, pass in null when calling <code>StorageVolume.createAccessIntent()</code> to request access to the entire volume, instead of a specific directory. <code>StorageVolume.createAccessIntent()</code> returns null if you pass in null to the primary volume, or if you pass in an invalid directory name. </p> <p>The following code snippet is an example of how to open the <code>Pictures</code> directory in the primary shared storage:</p> <pre> StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE); StorageVolume volume = sm.getPrimaryVolume(); Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES); startActivityForResult(intent, request_code); </pre> <p>The system attempts to grant access to the external directory, and if necessary confirms access with the user using a simplified UI:</p> <img src="{@docRoot}preview/images/scoped-folder-access-framed.png" srcset="{@docRoot}preview/images/scoped-folder-access-framed.png 1x, {@docRoot}preview/images/scoped-folder-access-framed_2x.png 2x" /> <p class="img-caption"><strong>Figure 1.</strong> An application requesting access to the Pictures directory.</p> <p>If the user grants access, the system calls your <code>onActivityResult()</code> override with a result code of <code>Activity.RESULT_OK</code>, and intent data that contains the URI. Use the provided URI to access directory information, similar to using URIs returned by the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a>.</p> <p>If the user doesn't grant access, the system calls your <code>onActivityResult()</code> override with a result code of <code>Activity.RESULT_CANCELED</code>, and null intent data.</p> <p class="note"><b>Note</b>: Getting access to a specific external directory also gains access to subdirectories within that directory.</p> <h2 id="removable">Accessing a Directory on Removable Media</h2> <p>To use Scoped Directory Access to access directories on removable media, first add a {@link android.content.BroadcastReceiver} that listens for the {@link android.os.Environment#MEDIA_MOUNTED} notification, for example:</p> <pre> <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> </pre> <p>When the user mounts removable media, like an SD card, the system sends a {@link android.os.Environment#MEDIA_MOUNTED} notification. This notification provides a <code>StorageVolume</code> object in the intent data that you can use to access directories on the removable media. The following example accesses the <code>Pictures</code> directory on removable media:</p> <pre> // 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); </pre> <h2 id="best">Best Practices</h2> <p>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 <code>getContentResolver().takePersistableUriPermssion()</code> with the directory access URI. The system will persist the URI and subsequent access requests will return <code>RESULT_OK</code> and not show confirmation UI to the user.</p> <p>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 <b>Don't ask again</b> checkbox:</p> <img src="{@docRoot}preview/images/scoped-folder-access-dont-ask.png" srcset="{@docRoot}preview/images/scoped-folder-access-dont-ask.png 1x, {@docRoot}preview/images/scoped-folder-access-dont-ask_2x.png 2x" /> <p class="img-caption"><strong>Figure 1.</strong> An application making a second request for access to removable media.</p> <p>If the user selects <b>Don't ask again</b> 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.</p>