blob: 932d8a97ccb637340d40d990a8e65c9f6c4873ab [file] [log] [blame]
Trevor Johns682c24e2016-04-12 10:13:47 -07001page.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>The following code snippet is an example of how to open the
48<code>Pictures</code> directory in the primary shared storage:</p>
49
50<pre>
51StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
52StorageVolume volume = sm.getPrimaryVolume();
53Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
54startActivityForResult(intent, request_code);
55</pre>
56
57<p>The system attempts to grant access to the external directory, and if
58necessary confirms access with the user using a simplified UI:</p>
59
60<img src="{@docRoot}preview/images/scoped-folder-access-framed.png"
61srcset="{@docRoot}preview/images/scoped-folder-access-framed.png 1x,
62{@docRoot}preview/images/scoped-folder-access-framed_2x.png 2x" />
63<p class="img-caption"><strong>Figure 1.</strong> An application requesting
64access to the Pictures directory.</p>
65
66<p>If the user grants access, the system calls your
67<code>onActivityResult()</code> override with a result code of
68<code>Activity.RESULT_OK</code>, and intent data that contains the URI. Use
69the provided URI to access directory information, similar to using URIs
70returned by the
71<a href="{@docRoot}guide/topics/providers/document-provider.html">Storage
72Access Framework</a>.</p>
73
74<p>If the user doesn't grant access, the system calls your
75<code>onActivityResult()</code> override with a result code of
76<code>Activity.RESULT_CANCELED</code>, and null intent data.</p>
77
78<p class="note"><b>Note</b>: Getting access to a specific external directory
79also gains access to subdirectories within that directory.</p>
80
81<h2 id="removable">Accessing a Directory on Removable Media</h2>
82
83<p>To use Scoped Directory Access to access directories on removable media,
84first add a {@link android.content.BroadcastReceiver} that listens for the
85{@link android.os.Environment#MEDIA_MOUNTED} notification, for example:</p>
86
87<pre>
88&lt;receiver
89 android:name=".MediaMountedReceiver"
90 android:enabled="true"
91 android:exported="true" &gt;
92 &lt;intent-filter&gt;
93 &lt;action android:name="android.intent.action.MEDIA_MOUNTED" /&gt;
94 &lt;data android:scheme="file" /&gt;
95 &lt;/intent-filter&gt;
96&lt;/receiver&gt;
97</pre>
98
99<p>When the user mounts removable media, like an SD card, the system sends a
100{@link android.os.Environment#MEDIA_MOUNTED} notification. This notification
101provides a <code>StorageVolume</code> object in the intent data that you can
102use to access directories on the removable media. The following example
103accesses the <code>Pictures</code> directory on removable media:</p>
104
105<pre>
106// BroadcastReceiver has already cached the MEDIA_MOUNTED
107// notification Intent in mediaMountedIntent
108StorageVolume volume = (StorageVolume)
109 mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
110volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
111startActivityForResult(intent, request_code);
112</pre>
113
114<h2 id="best">Best Practices</h2>
115
116<p>Where possible, persist the external directory access URI so you don't have
117to repeatedly ask the user for access. Once the user has granted access, call
118<code>getContentResolver().takePersistableUriPermssion()</code> with the
119directory access URI. The system will persist the URI and subsequent access
120requests will return <code>RESULT_OK</code> and not show confirmation UI to the
121user.</p>
122
123<p>If the user denies access to an external directory, do not immediately
124request access again. Repeatedly insisting on access results in a poor user
125experience.</p>