Home | History | Annotate | Download | only in articles
      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 &lt;receiver
    112     android:name=".MediaMountedReceiver"
    113     android:enabled="true"
    114     android:exported="true" &gt;
    115     &lt;intent-filter&gt;
    116         &lt;action android:name="android.intent.action.MEDIA_MOUNTED" /&gt;
    117         &lt;data android:scheme="file" /&gt;
    118     &lt;/intent-filter&gt;
    119 &lt;/receiver&gt;
    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>