blob: 814692d695cfb968731d00c5dd04525a32a68783 [file] [log] [blame]
Daniel Yu29e6ae62016-02-22 16:29:34 -08001page.title=Scoped Directory Access
2page.keywords=preview,sdk,scoped directory access
3page.tags=androidn
4
5@jd:body
6
Joe Fernandez98b0ec62016-03-08 22:32:51 -08007<div id="qv-wrapper">
8<div id="qv">
Daniel Yu29e6ae62016-02-22 16:29:34 -08009 <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
David Friedmanfffa8ac2016-03-07 22:13:29 -080035<p>Android N provides a new simplified API to access
Daniel Yu29e6ae62016-02-22 16:29:34 -080036common 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
Daniel Yu9ec08f82016-04-01 14:29:40 -070047<p>
48On secondary volumes, such as external SD cards, pass in null when calling
49<code>StorageVolume.createAccessIntent()</code> to request access to the entire
50volume, instead of a specific directory.
51<code>StorageVolume.createAccessIntent()</code> returns null if you pass in
52null to the primary volume, or if you pass in an invalid directory name.
53</p>
54
Daniel Yu29e6ae62016-02-22 16:29:34 -080055<p>The following code snippet is an example of how to open the
56<code>Pictures</code> directory in the primary shared storage:</p>
57
58<pre>
59StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
60StorageVolume volume = sm.getPrimaryVolume();
61Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
62startActivityForResult(intent, request_code);
63</pre>
64
65<p>The system attempts to grant access to the external directory, and if
66necessary confirms access with the user using a simplified UI:</p>
67
68<img src="{@docRoot}preview/images/scoped-folder-access-framed.png"
69srcset="{@docRoot}preview/images/scoped-folder-access-framed.png 1x,
70{@docRoot}preview/images/scoped-folder-access-framed_2x.png 2x" />
71<p class="img-caption"><strong>Figure 1.</strong> An application requesting
72access to the Pictures directory.</p>
73
74<p>If the user grants access, the system calls your
75<code>onActivityResult()</code> override with a result code of
76<code>Activity.RESULT_OK</code>, and intent data that contains the URI. Use
77the provided URI to access directory information, similar to using URIs
78returned by the
79<a href="{@docRoot}guide/topics/providers/document-provider.html">Storage
80Access Framework</a>.</p>
81
82<p>If the user doesn't grant access, the system calls your
83<code>onActivityResult()</code> override with a result code of
84<code>Activity.RESULT_CANCELED</code>, and null intent data.</p>
85
86<p class="note"><b>Note</b>: Getting access to a specific external directory
87also gains access to subdirectories within that directory.</p>
88
89<h2 id="removable">Accessing a Directory on Removable Media</h2>
90
91<p>To use Scoped Directory Access to access directories on removable media,
92first add a {@link android.content.BroadcastReceiver} that listens for the
93{@link android.os.Environment#MEDIA_MOUNTED} notification, for example:</p>
94
95<pre>
96&lt;receiver
97 android:name=".MediaMountedReceiver"
98 android:enabled="true"
99 android:exported="true" &gt;
100 &lt;intent-filter&gt;
101 &lt;action android:name="android.intent.action.MEDIA_MOUNTED" /&gt;
102 &lt;data android:scheme="file" /&gt;
103 &lt;/intent-filter&gt;
104&lt;/receiver&gt;
105</pre>
106
107<p>When the user mounts removable media, like an SD card, the system sends a
108{@link android.os.Environment#MEDIA_MOUNTED} notification. This notification
109provides a <code>StorageVolume</code> object in the intent data that you can
110use to access directories on the removable media. The following example
111accesses the <code>Pictures</code> directory on removable media:</p>
112
113<pre>
114// BroadcastReceiver has already cached the MEDIA_MOUNTED
115// notification Intent in mediaMountedIntent
116StorageVolume volume = (StorageVolume)
117 mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
118volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
119startActivityForResult(intent, request_code);
120</pre>
121
122<h2 id="best">Best Practices</h2>
123
124<p>Where possible, persist the external directory access URI so you don't have
125to repeatedly ask the user for access. Once the user has granted access, call
126<code>getContentResolver().takePersistableUriPermssion()</code> with the
127directory access URI. The system will persist the URI and subsequent access
128requests will return <code>RESULT_OK</code> and not show confirmation UI to the
129user.</p>
130
131<p>If the user denies access to an external directory, do not immediately
132request access again. Repeatedly insisting on access results in a poor user
Daniel Yu9ec08f82016-04-01 14:29:40 -0700133experience. If a request is denied by the user, and the app requests access
134again, the UI displays a <b>Don't ask again</b> checkbox:</p>
135
136<img src="{@docRoot}preview/images/scoped-folder-access-dont-ask.png"
137srcset="{@docRoot}preview/images/scoped-folder-access-dont-ask.png 1x,
138{@docRoot}preview/images/scoped-folder-access-dont-ask_2x.png 2x" />
139<p class="img-caption"><strong>Figure 1.</strong> An application making a
140second request for access to removable media.</p>
141
142<p>If the user selects <b>Don't ask again</b> and denies the request, all
143future requests for the given directory from your app will be automatically
144denied, and no request UI will be presented to the user.</p>