page.title=Acceso a directorios determinados
page.keywords=preview,sdk,scoped directory access
page.tags=androidn

@jd:body

<div id="qv-wrapper">
<div id="qv">
  <h2>En este documento</h2>
  <ol>
    <li><a href="#accessing">Acceder a un directorio de almacenamiento externo</a></li>
    <li><a href="#removable">Acceder a un directorio de un medio extraíble</a></li>
    <li><a href="#best">Prácticas recomendadas</a></li>
  </ol>
</div>
</div>

<p>Las aplicaciones como las aplicaciones de fotografía generalmente solo necesitan acceso a directorios de
almacenamiento externo, como el directorio <code>Pictures</code>. Los métodos
existentes para acceder a almacenamiento externo no están diseñados para brindar un
acceso fácil a determinados directorios para estos tipos de aplicaciones. Por ejemplo:</p>

<ul>
<li>Solicitar {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}
o {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} en tu manifiesto
permite el acceso a todos los directorios públicos de un almacenamiento externo, lo cual podría ser
un acceso mayor que el que necesita tu aplicación.</li>
<li>Usar el
<a href="{@docRoot}guide/topics/providers/document-provider.html">framework
de acceso al almacenamiento</a> generalmente implica que el usuario seleccione directorios
mediante un sistema de IU, lo cual no es necesario si tu aplicación siempre accede al mismo
directorio externo.</li>
</ul>

<p>Android N brinda una API nueva y simplificada para acceder a
 directorios de almacenamiento externo comunes. </p>

<h2 id="accessing">Acceder a un directorio de almacenamiento externo</h2>

<p>Usa la clase <code>StorageManager</code> para obtener la instancia de
<code>StorageVolume</code> correcta. Luego, crea una intent llamando al
 método <code>StorageVolume.createAccessIntent()</code> de esa instancia.
Usa esta intención para acceder a directorios de almacenamiento externo. Para obtener una lista de
todos los volúmenes disponibles, incluidos los volúmenes de medios extraíbles, usa 
<code>StorageManager.getVolumesList()</code>.</p>

<p>Si tienes información sobre un archivo específico, usa 
<code>StorageManager.getStorageVolume(File)</code> para obtener el 
<code>StorageVolume</code> que contiene el archivo. Llama a 
<code>createAccessIntent()</code> en este <code>StorageVolume</code> para acceder al 
directorio de almacenamiento externo del archivo.</p>

<p>
En el caso de los volúmenes secundarios, como las tarjetas SD externas, pasa un valor nulo cuando llames a 
<code>StorageVolume.createAccessIntent()</code> para solicitar acceso al volumen
 completo, en lugar de un directorio específico.
<code>StorageVolume.createAccessIntent()</code> regresa un valor nulo si pasas un
 valor nulo para el volumen principal o si pasas un nombre de directorio no válido.
</p>

<p>El siguiente fragmento de código es un ejemplo de cómo abrir el
directorio <code>Pictures</code> en el almacenamiento compartido principal:</p>

<pre>
StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
StorageVolume volume = sm.getPrimaryVolume();
Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);
</pre>

<p>El sistema intenta otorgar acceso al directorio externo y, si
es necesario, confirma el acceso con el usuario usando una IU simplificada:</p>

<img src="{@docRoot}preview/images/scoped-folder-access-framed.png" srcset="{@docRoot}preview/images/scoped-folder-access-framed.png 1x,
{@docRoot}preview/images/scoped-folder-access-framed_2x.png 2x" />
<p class="img-caption"><strong>Imagen 1.</strong> Una aplicación solicitando
acceso al directorio Pictures.</p>

<p>Si el usuario otorga el acceso, el sistema llama a tu
 anulación de <code>onActivityResult()</code> con un código resultante de
<code>Activity.RESULT_OK</code> y datos de intents que contienen el URI. Usa
el URI brindado para acceder a la información del directorio. Es similar a usar URI
generados por el
<a href="{@docRoot}guide/topics/providers/document-provider.html">framework
de acceso al almacenamiento</a>.</p>

<p>Si el usuario no otorga el acceso, el sistema llama a tu
anulación de <code>onActivityResult()</code> con un código resultante de
<code>Activity.RESULT_CANCELED</code> y datos de intents nulos.</p>

<p class="note"><b>Nota</b>: Obtener acceso a un directorio externo específico
también otorga el acceso a los subdirectorios de ese directorio.</p>

<h2 id="removable">Acceder a un directorio de un medio extraíble</h2>

<p>Para usar el acceso a directorios determinados para acceder a directorios de medios extraíbles,
primero debes agregar un {@link android.content.BroadcastReceiver} que escuche la
notificación{@link android.os.Environment#MEDIA_MOUNTED}, por ejemplo:</p>

<pre>
&lt;receiver
    android:name=".MediaMountedReceiver"
    android:enabled="true"
    android:exported="true" &gt;
    &lt;intent-filter&gt;
        &lt;action android:name="android.intent.action.MEDIA_MOUNTED" /&gt;
        &lt;data android:scheme="file" /&gt;
    &lt;/intent-filter&gt;
&lt;/receiver&gt;
</pre>

<p>Cuando el usuario conecta un medio extraíble, como una tarjeta SD, el sistema envía una
notificación{@link android.os.Environment#MEDIA_MOUNTED}. Esta notificación
brinda un objeto <code>StorageVolume</code> en los datos de intents que puedes
usar para acceder a directorios del medio extraíble. El siguiente ejemplo
accede al directorio <code>Pictures</code> de medios extraíbles:</p>

<pre>
// BroadcastReceiver has already cached the MEDIA_MOUNTED
// notification Intent in mediaMountedIntent
StorageVolume volume = (StorageVolume)
    mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);
</pre>

<h2 id="best">Prácticas recomendadas</h2>

<p>Cuando sea posible, sigue usando el URI de acceso a directorios externos de modo que no tengas
que solicitarle acceso al usuario continuamente. Una vez que el usuario haya otorgado el acceso, llama a
<code>getContentResolver().takePersistableUriPermssion()</code> con el
URI de acceso a directorios. El sistema continuará el URI, y las siguientes solicitudes
de acceso generarán <code>RESULT_OK</code> y no le mostrarán una IU de confirmación al
usuario.</p>

<p>Si el usuario deniega el acceso a un directorio externo, no vuelvas a solicitar el
acceso inmediatamente. Hacer esto provocaría una mala experiencia
de usuario. Si el usuario deniega una solicitud y la aplicación solicita acceso
 nuevamente, aparece la casilla de verificación <b>Don't ask again</b> en la IU:</p>

<img src="{@docRoot}preview/images/scoped-folder-access-dont-ask.png" srcset="{@docRoot}preview/images/scoped-folder-access-dont-ask.png 1x,
{@docRoot}preview/images/scoped-folder-access-dont-ask_2x.png 2x" />
<p class="img-caption"><strong>Figura 1.</strong> Una aplicación que presenta una 
segunda solicitud para obtener acceso a medios extraíbles.</p>

<p>Si el usuario selecciona <b>Don't ask again</b> y deniega la solicitud, todas las
solicitudes futuras que presente la aplicación para el directorio determinado se denegarán
 automáticamente, y el usuario no recibirá ninguna IU de solicitud.</p>