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