1page.title=Scoped Directory Access 2page.keywords=preview,sdk,scoped directory access 3page.tags=androidn 4 5@jd:body 6 7<div id="qv-wrapper"> 8<div id="qv"> 9 <h2>In this document</h2> 10 <ol> 11 <li><a href="#accessing">Accessing an External Storage Directory</a></li> 12 <li><a href="#removable">Accessing a Directory on Removable Media</a></li> 13 <li><a href="#best">Best Practices</a></li> 14 </ol> 15</div> 16</div> 17 18<p>Apps such as photo apps usually just need access to specific directories in 19external storage, such as the <code>Pictures</code> directory. Existing 20approaches to accessing external storage aren't designed to easily provide 21targeted directory access for these types of apps. For example:</p> 22 23<ul> 24<li>Requesting {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} 25or {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} in your manifest 26allows access to all public directories on external storage, which might be 27more access than what your app needs.</li> 28<li>Using the 29<a href="{@docRoot}guide/topics/providers/document-provider.html">Storage 30Access Framework</a> usually makes your user pick directories 31via a system UI, which is unnecessary if your app always accesses the same 32external directory.</li> 33</ul> 34 35<p>Android N provides a new simplified API to access 36common external storage directories. </p> 37 38<h2 id="accessing">Accessing an External Storage Directory</h2> 39 40<p>Use the <code>StorageManager</code> class to get the appropriate 41<code>StorageVolume</code> instance. Then, create an intent by calling the 42<code>StorageVolume.createAccessIntent()</code> method of that instance. 43Use this intent to access external storage directories. To get a list of 44all available volumes, including removable media volumes, use 45<code>StorageManager.getVolumesList()</code>.</p> 46 47<p>If you have information about a specific file, use 48<code>StorageManager.getStorageVolume(File)</code> to get the 49<code>StorageVolume</code> that contains the file. Call 50<code>createAccessIntent()</code> on this <code>StorageVolume</code> to access 51the external storage directory for the file.</p> 52 53<p> 54On secondary volumes, such as external SD cards, pass in null when calling 55<code>StorageVolume.createAccessIntent()</code> to request access to the entire 56volume, instead of a specific directory. 57<code>StorageVolume.createAccessIntent()</code> returns null if you pass in 58null to the primary volume, or if you pass in an invalid directory name. 59</p> 60 61<p>The following code snippet is an example of how to open the 62<code>Pictures</code> directory in the primary shared storage:</p> 63 64<pre> 65StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE); 66StorageVolume volume = sm.getPrimaryVolume(); 67Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES); 68startActivityForResult(intent, request_code); 69</pre> 70 71<p>The system attempts to grant access to the external directory, and if 72necessary confirms access with the user using a simplified UI:</p> 73 74<img src="{@docRoot}preview/images/scoped-folder-access-framed.png" 75srcset="{@docRoot}preview/images/scoped-folder-access-framed.png 1x, 76{@docRoot}preview/images/scoped-folder-access-framed_2x.png 2x" /> 77<p class="img-caption"><strong>Figure 1.</strong> An application requesting 78access to the Pictures directory.</p> 79 80<p>If the user grants access, the system calls your 81<code>onActivityResult()</code> override with a result code of 82<code>Activity.RESULT_OK</code>, and intent data that contains the URI. Use 83the provided URI to access directory information, similar to using URIs 84returned by the 85<a href="{@docRoot}guide/topics/providers/document-provider.html">Storage 86Access Framework</a>.</p> 87 88<p>If the user doesn't grant access, the system calls your 89<code>onActivityResult()</code> override with a result code of 90<code>Activity.RESULT_CANCELED</code>, and null intent data.</p> 91 92<p class="note"><b>Note</b>: Getting access to a specific external directory 93also gains access to subdirectories within that directory.</p> 94 95<h2 id="removable">Accessing a Directory on Removable Media</h2> 96 97<p>To use Scoped Directory Access to access directories on removable media, 98first add a {@link android.content.BroadcastReceiver} that listens for the 99{@link android.os.Environment#MEDIA_MOUNTED} notification, for example:</p> 100 101<pre> 102<receiver 103 android:name=".MediaMountedReceiver" 104 android:enabled="true" 105 android:exported="true" > 106 <intent-filter> 107 <action android:name="android.intent.action.MEDIA_MOUNTED" /> 108 <data android:scheme="file" /> 109 </intent-filter> 110</receiver> 111</pre> 112 113<p>When the user mounts removable media, like an SD card, the system sends a 114{@link android.os.Environment#MEDIA_MOUNTED} notification. This notification 115provides a <code>StorageVolume</code> object in the intent data that you can 116use to access directories on the removable media. The following example 117accesses the <code>Pictures</code> directory on removable media:</p> 118 119<pre> 120// BroadcastReceiver has already cached the MEDIA_MOUNTED 121// notification Intent in mediaMountedIntent 122StorageVolume volume = (StorageVolume) 123 mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME); 124volume.createAccessIntent(Environment.DIRECTORY_PICTURES); 125startActivityForResult(intent, request_code); 126</pre> 127 128<h2 id="best">Best Practices</h2> 129 130<p>Where possible, persist the external directory access URI so you don't have 131to repeatedly ask the user for access. Once the user has granted access, call 132<code>getContentResolver().takePersistableUriPermssion()</code> with the 133directory access URI. The system will persist the URI and subsequent access 134requests will return <code>RESULT_OK</code> and not show confirmation UI to the 135user.</p> 136 137<p>If the user denies access to an external directory, do not immediately 138request access again. Repeatedly insisting on access results in a poor user 139experience. If a request is denied by the user, and the app requests access 140again, the UI displays a <b>Don't ask again</b> checkbox:</p> 141 142<img src="{@docRoot}preview/images/scoped-folder-access-dont-ask.png" 143srcset="{@docRoot}preview/images/scoped-folder-access-dont-ask.png 1x, 144{@docRoot}preview/images/scoped-folder-access-dont-ask_2x.png 2x" /> 145<p class="img-caption"><strong>Figure 1.</strong> An application making a 146second request for access to removable media.</p> 147 148<p>If the user selects <b>Don't ask again</b> and denies the request, all 149future requests for the given directory from your app will be automatically 150denied, and no request UI will be presented to the user.</p>