Uso del acceso a directorios determinados

Por lo general, las apps como las de fotografía solo necesitan acceso a directorios de almacenamiento externo específicos, como Pictures. Los métodos existentes para acceder a almacenamiento externo no están diseñados para brindar acceso sencillo a ciertos directorios para esos tipos de apps. Por ejemplo:

  • Solicitar READ_EXTERNAL_STORAGE o WRITE_EXTERNAL_STORAGE en tu manifiesto permite el acceso a todos los directorios públicos de un almacenamiento externo, lo que podría representar un acceso mayor que el que necesita tu app.
  • Por lo general, usar el marco de trabajo de acceso al almacenamiento implica que el usuario deba seleccionar directorios a través de una IU del sistema, lo que no es necesario si tu app siempre accede al mismo directorio externo.

Android 7.0 proporciona una API simplificada para acceder a directorios de almacenamiento externos comunes.

Acceso a un directorio de almacenamiento externo

Usa la clase StorageManager para obtener la instancia correcta de StorageVolume. Luego, crea una intent invocando al método StorageVolume.createAccessIntent() de esa instancia. Usa esa intent para acceder a directorios de almacenamiento externo. Para obtener una lista de todos los volúmenes disponibles, incluidos de los de medios extraíbles, usa StorageManager.getStorageVolumes().

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

En el caso de los volúmenes secundarios, como las tarjetas SD externas, transfiere un valor nulo cuando llames a createAccessIntent() para solicitar acceso al volumen completo, en lugar de un directorio específico. createAccessIntent() muestra un valor nulo si transfieres un valor nulo al volumen principal o un nombre de directorio no válido.

El siguiente fragmento muestra cómo abrir el directorio Pictures en el almacenamiento compartido principal:

Kotlin

    val sm = getSystemService(Context.STORAGE_SERVICE) as StorageManager
    val volume: StorageVolume = sm.primaryStorageVolume
    volume.createAccessIntent(Environment.DIRECTORY_PICTURES).also { intent ->
        startActivityForResult(intent, request_code)
    }
    

Java

    StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
    StorageVolume volume = sm.getPrimaryStorageVolume();
    Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
    startActivityForResult(intent, request_code);
    

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

Figura 1: Aplicación solicitando acceso al directorio Pictures

Si el usuario otorga el acceso, el sistema invoca a la anulación de onActivityResult() con un código de resultado de RESULT_OK y datos de intents que contienen el URI. Usa el URI provisto para acceder a la información del directorio. Esto es similar a usar URI que muestra el marco de trabajo de acceso al almacenamiento.

Si el usuario no otorga el acceso, el sistema invoca a la anulación de onActivityResult() con un código de resultado de RESULT_CANCELED y datos de intents nulos.

Obtener acceso a un directorio externo específico también permite el acceso a los subdirectorios de ese directorio.

Acceso a un directorio de un medio extraíble

Para usar el acceso a directorios determinados a fin de acceder a directorios de medios extraíbles, primero debes agregar un BroadcastReceiver que reciba la notificación MEDIA_MOUNTED. Por ejemplo:

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

Cuando el usuario activa un medio extraíble, como una tarjeta SD, el sistema envía una notificación MEDIA_MOUNTED. Esta notificación proporciona un objeto StorageVolume en los datos del intent que puedes usar para acceder a directorios del medio extraíble. En el siguiente ejemplo se accede al directorio Pictures de medios extraíbles:

Kotlin

    // BroadcastReceiver has already cached the MEDIA_MOUNTED
    // notification Intent in mediaMountedIntent
    val volume =
        mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME) as StorageVolume
    volume.createAccessIntent(Environment.DIRECTORY_PICTURES).also { intent ->
        startActivityForResult(intent, request_code)
    }
    

Java

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

Recomendaciones

Cuando sea posible, conserva el URI de acceso a directorios externos de modo que no debas solicitar acceso al usuario continuamente. Una vez que el usuario haya otorgado el acceso, llama a getContentResolver() y, con el ContentResolver que se muestra, invoca a takePersistableUriPermission() usando el URI de acceso a directorios. El sistema conservará el URI, y las solicitudes de acceso siguientes generarán RESULT_OK y no le mostrarán al usuario una IU de confirmación.

Si el usuario rechaza el acceso a un directorio externo, no vuelvas a solicitar acceso de inmediato. Insistir con la solicitud de acceso tendría efectos negativos en la experiencia del usuario. Si el usuario rechaza una solicitud y la app solicita acceso nuevamente, aparecerá la casilla de verificación No volver a preguntar en la IU:

Figura 1: Una aplicación realiza una segunda solicitud de acceso a medios extraíbles

Si el usuario selecciona No volver a preguntar y rechaza la solicitud, todas las solicitudes futuras que se presenten para el directorio en cuestión se rechazarán automáticamente y el usuario no recibirá ninguna IU de solicitud.