Cómo otorgar acceso parcial a fotos y videos

En Android 14, se introduce el acceso a fotos seleccionadas, que permite a los usuarios otorgar a las apps acceso a imágenes y videos específicos de su biblioteca, en lugar de dar acceso a todo el contenido multimedia de un tipo determinado.

Este cambio solo se habilita si tu app se orienta a Android 14 (nivel de API 34) o versiones posteriores. Si aún no usas el selector de fotos, te recomendamos que lo implementes en tu app para proporcionar una experiencia coherente para seleccionar imágenes y videos que también mejore la privacidad del usuario sin tener que solicitar ningún permiso de almacenamiento.

Si mantienes tu propio selector de galería mediante permisos de almacenamiento y necesitas mantener el control total sobre tu implementación, adapta tu implementación para usar el nuevo permiso READ_MEDIA_VISUAL_USER_SELECTED. Si tu app no usa el permiso nuevo, el sistema la ejecuta en un modo de compatibilidad.

SDK de destino Se declaró READ_MEDIA_VISUAL_USER_SELECTED Se habilitó el acceso a las fotos seleccionadas Comportamiento de UX
SDK 33 No No N/A
Controlado por la app
SDK 34 No Controlado por el sistema (comportamiento de compatibilidad)
Controlado por la app

Para crear tu propio selector de galería, se requiere un desarrollo y mantenimiento extensos, y tu app debe solicitar permisos de almacenamiento para obtener el consentimiento explícito del usuario. Los usuarios pueden rechazar estas solicitudes o, si tu app se ejecuta en un dispositivo con Android 14 y esta se orienta a Android 14 (nivel de API 34) o versiones posteriores, limitar el acceso al contenido multimedia seleccionado. En la siguiente imagen, se muestra un ejemplo de solicitud de permisos y selección de contenido multimedia con las opciones nuevas.

La extensión .
Figura 1: El nuevo diálogo permite al usuario seleccionar fotos y videos específicos que quiera que estén disponibles para tu app, además de las opciones habituales para otorgar acceso completo o denegar todo el acceso.

En esta sección, se muestra el enfoque recomendado para crear tu propio selector de galería mediante MediaStore. Si ya mantienes un selector de galería para tu app y necesitas mantener un control total, puedes usar estos ejemplos para adaptar tu implementación. Si no actualizas tu implementación para que controle el Acceso a fotos seleccionadas, el sistema ejecutará tu app en un modo de compatibilidad.

Solicita permisos

Primero, solicita los permisos de almacenamiento correctos en el manifiesto de Android, según la versión del SO:

<!-- Devices running Android 12L (API level 32) or lower  -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

<!-- Devices running Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- To handle the reselection within the app on devices running Android 14
     or higher if your app targets Android 14 (API level 34) or higher.  -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

Luego, solicita los permisos de tiempo de ejecución correctos, según la versión del SO:

// Register ActivityResult handler
val requestPermissions = registerForActivityResult(RequestMultiplePermissions()) { results ->
    // Handle permission requests results
    // See the permission example in the Android platform samples: https://github.com/android/platform-samples
}

// Permission request logic
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
    requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED))
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO))
} else {
    requestPermissions.launch(arrayOf(READ_EXTERNAL_STORAGE))
}

Algunas apps no necesitan permisos

A partir de Android 10 (nivel de API 29), las apps ya no necesitan permisos de almacenamiento para agregar archivos al almacenamiento compartido. Esto significa que las apps pueden agregar imágenes a la galería, grabar videos y guardarlos en el almacenamiento compartido, o descargar facturas en PDF sin tener que solicitar permisos de almacenamiento. Si tu app solo agrega archivos al almacenamiento compartido y no consulta imágenes ni videos, debes dejar de solicitar permisos de almacenamiento y establecer un maxSdkVersion de nivel de API 28 en tu AndroidManifest.xml:

<!-- No permission is needed to add files to shared storage on Android 10 (API level 29) or higher  -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />

Cómo controlar la reselección de contenido multimedia

Con la función Acceso a fotos seleccionadas en Android 14, tu app debe adoptar el nuevo permiso READ_MEDIA_VISUAL_USER_SELECTED para controlar la reselección de contenido multimedia y actualizar la interfaz de tu app para permitir que los usuarios otorguen acceso a un conjunto diferente de imágenes y videos. En la siguiente imagen, se muestra un ejemplo de cómo solicitar permisos y volver a seleccionar contenido multimedia:

La extensión .
Figura 2: El nuevo diálogo también permite que el usuario vuelva a seleccionar las fotos y los videos que desea que estén disponibles para tu app.

Cuando se abre el diálogo de selección, se muestran fotos, videos o ambos, según los permisos solicitados. Por ejemplo, si solicitas el permiso READ_MEDIA_VIDEO sin el permiso READ_MEDIA_IMAGES, solo aparecerán videos en la IU para que los usuarios seleccionen archivos.

// Allow the user to select only videos
requestPermissions.launch(arrayOf(READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED))

Puedes comprobar si tu app tiene acceso completo, parcial o denegado a la biblioteca de fotos del dispositivo y actualizar la interfaz según corresponda. Solicita estos permisos cuando la app necesite acceso al almacenamiento, en lugar de hacerlo al inicio. Ten en cuenta que la concesión de permisos se puede cambiar entre las devoluciones de llamada del ciclo de vida onStart y onResume de la app, ya que el usuario puede cambiar el acceso desde la configuración sin cerrar la app.

if (
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
    (
        ContextCompat.checkSelfPermission(context, READ_MEDIA_IMAGES) == PERMISSION_GRANTED ||
        ContextCompat.checkSelfPermission(context, READ_MEDIA_VIDEO) == PERMISSION_GRANTED
    )
) {
    // Full access on Android 13 (API level 33) or higher
} else if (
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
    ContextCompat.checkSelfPermission(context, READ_MEDIA_VISUAL_USER_SELECTED) == PERMISSION_GRANTED
) {
    // Partial access on Android 14 (API level 34) or higher
}  else if (ContextCompat.checkSelfPermission(context, READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
    // Full access up to Android 12 (API level 32)
} else {
    // Access denied
}

Cómo consultar la biblioteca del dispositivo

Después de verificar que tienes acceso a los permisos de almacenamiento correctos, puedes interactuar con MediaStore para consultar la biblioteca del dispositivo (el mismo enfoque funciona sin importar si el acceso otorgado es parcial o completo):

data class Media(
    val uri: Uri,
    val name: String,
    val size: Long,
    val mimeType: String,
)

// Run the querying logic in a coroutine outside of the main thread to keep the app responsive.
// Keep in mind that this code snippet is querying only images of the shared storage.
suspend fun getImages(contentResolver: ContentResolver): List<Media> = withContext(Dispatchers.IO) {
    val projection = arrayOf(
        Images.Media._ID,
        Images.Media.DISPLAY_NAME,
        Images.Media.SIZE,
        Images.Media.MIME_TYPE,
    )

    val collectionUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Query all the device storage volumes instead of the primary only
        Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
    } else {
        Images.Media.EXTERNAL_CONTENT_URI
    }

    val images = mutableListOf<Media>()

    contentResolver.query(
        collectionUri,
        projection,
        null,
        null,
        "${Images.Media.DATE_ADDED} DESC"
    )?.use { cursor ->
        val idColumn = cursor.getColumnIndexOrThrow(Images.Media._ID)
        val displayNameColumn = cursor.getColumnIndexOrThrow(Images.Media.DISPLAY_NAME)
        val sizeColumn = cursor.getColumnIndexOrThrow(Images.Media.SIZE)
        val mimeTypeColumn = cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE)

        while (cursor.moveToNext()) {
            val uri = ContentUris.withAppendedId(collectionUri, cursor.getLong(idColumn))
            val name = cursor.getString(displayNameColumn)
            val size = cursor.getLong(sizeColumn)
            val mimeType = cursor.getString(mimeTypeColumn)

            val image = Media(uri, name, size, mimeType)
            images.add(image)
        }
    }

    return@withContext images
}

Este fragmento de código se simplifica para ilustrar cómo interactuar con MediaStore. En una app lista para la producción, usa la paginación con algo como la biblioteca de Paging para garantizar un buen rendimiento.

El acceso a las fotos y los videos se conserva cuando se actualiza el dispositivo

En los casos en que tu app esté en un dispositivo que se actualice de una versión anterior de Android a Android 14, el sistema conservará el acceso completo a las fotos y los videos del usuario, y le otorgará algunos permisos automáticamente. El comportamiento exacto depende de los permisos que se le otorguen a tu app antes de que el dispositivo se actualice a Android 14.

Permisos de Android 13

Ten en cuenta la siguiente situación:

  1. Tu app se instaló en un dispositivo que ejecuta Android 13.
  2. El usuario otorgó los permisos READ_MEDIA_IMAGES y READ_MEDIA_VIDEO a tu app.
  3. Luego, el dispositivo se actualiza a Android 14 mientras la app está instalada.
  4. Tu app comienza a orientarse a Android 14 (nivel de API 34) o versiones posteriores.

En este caso, tu app todavía tiene acceso completo a las fotos y los videos del usuario. El sistema también mantiene automáticamente los permisos READ_MEDIA_IMAGES y READ_MEDIA_VIDEO para tu app.

Permisos de Android 12 y versiones anteriores

Ten en cuenta la siguiente situación:

  1. Tu app se instaló en un dispositivo que ejecuta Android 13.
  2. El usuario otorgó los permisos READ_EXTERNAL_STORAGE o WRITE_EXTERNAL_STORAGE a tu app.
  3. Luego, el dispositivo se actualiza a Android 14 mientras la app está instalada.
  4. Tu app comienza a orientarse a Android 14 (nivel de API 34) o versiones posteriores.

En este caso, tu app todavía tiene acceso completo a las fotos y los videos del usuario. El sistema también otorga los permisos READ_MEDIA_IMAGES y READ_MEDIA_VIDEO a tu app automáticamente.

Prácticas recomendadas

En esta sección, se incluyen varias prácticas recomendadas para usar el permiso READ_MEDIA_VISUAL_USER_SELECTED. Para obtener más información, consulta nuestras prácticas recomendadas para permisos.

No almacenes el estado del permiso de forma permanente

No almacenes el estado del permiso de forma permanente, lo que incluye SharedPreferences o DataStore. Es posible que el estado almacenado no esté sincronizado con el estado real. El estado del permiso puede cambiar después del restablecimiento del permiso, la hibernación de la app, un cambio iniciado por el usuario en la configuración de la app o cuando esta pasa a segundo plano. En su lugar, verifica los permisos de almacenamiento con ContextCompat.checkSelfPermission().

No supongas que tienes acceso completo a fotos y videos

En función de los cambios introducidos en Android 14, es posible que tu app solo tenga acceso parcial a la biblioteca de fotos del dispositivo. Si la app almacena en caché datos de MediaStore cuando se realiza una consulta con ContentResolver, es posible que la caché no esté actualizada.

  • Siempre consulta MediaStore con ContentResolver, en lugar de depender de una caché almacenada.
  • Mantén los resultados en la memoria mientras la app esté en primer plano.
  • Actualiza los resultados cuando tu app pase por el ciclo de vida de la app de onResume, ya que el usuario podría cambiar de acceso completo a acceso parcial mediante la configuración de permisos.

Trata el acceso de URI como temporario

Si el usuario elige Seleccionar fotos y videos en el diálogo de permisos del sistema, vencerá el acceso de tu app a las fotos y los videos seleccionados. Tu app siempre debe controlar el caso de no tener acceso a ningún Uri, independientemente de su autoridad.

Filtrar los tipos de medios seleccionables por permiso

El diálogo de selección tiene en cuenta el tipo de permiso solicitado:

  • Si solicitas solo READ_MEDIA_IMAGES, solo se muestran imágenes que se pueden seleccionar.
  • Si solicitas solo READ_MEDIA_VIDEO, solo se muestra el video que se puede seleccionar.
  • Si solicitas READ_MEDIA_IMAGES y READ_MEDIA_VIDEO, se muestra toda la biblioteca de fotos que se puede seleccionar.

Según los casos de uso de tu app, debes asegurarte de solicitar los permisos adecuados para evitar una experiencia del usuario deficiente. Si una función solo espera que se seleccionen videos, asegúrate de solicitar solo READ_MEDIA_VIDEO.

Solicita permisos en una sola operación

Para evitar que los usuarios vean varios cuadros de diálogo del tiempo de ejecución del sistema, solicita los permisos READ_MEDIA_VISUAL_USER_SELECTED, ACCESS_MEDIA_LOCATION y "lectura de contenido multimedia" (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO o ambos) en una sola operación.

Permitir que los usuarios administren su selección

Cuando el usuario elige el modo de acceso parcial, la app no debe suponer que la biblioteca de fotos del dispositivo está vacía, y debería permitir que el usuario otorgue más archivos.

El usuario puede decidir cambiar del acceso completo al acceso parcial a través de la configuración de permisos sin otorgar acceso a algunos archivos de medios visuales.

Modo de compatibilidad

Si mantienes tu propio selector de galería con permisos de almacenamiento, pero no adaptaste tu app para usar el nuevo permiso READ_MEDIA_VISUAL_USER_SELECTED, el sistema ejecutará tu app en un modo de compatibilidad cada vez que el usuario necesite seleccionar o volver a seleccionar contenido multimedia.

Comportamiento durante la selección de contenido multimedia inicial

Durante la selección inicial, si un usuario elige "Seleccionar fotos y videos" (consulta la figura 1), se otorgan los permisos READ_MEDIA_IMAGES y READ_MEDIA_VIDEO durante la sesión de la app, lo que proporciona un otorgamiento temporal de permisos y acceso temporal a las fotos y los videos seleccionados por el usuario. Cuando tu app pasa a segundo plano, o cuando el usuario la finaliza de manera activa, el sistema con el tiempo rechaza estos permisos. Este comportamiento es como cualquier otro permiso único.

Comportamiento durante la reselección de contenido multimedia

Si tu app necesita acceder a fotos y videos adicionales más adelante, debes volver a solicitar el permiso READ_MEDIA_IMAGES o READ_MEDIA_VIDEO de forma manual. El sistema sigue el mismo flujo que con la solicitud de permiso inicial y les solicita a los usuarios que seleccionen fotos y videos (consulta la figura 2).

Si tu app sigue las prácticas recomendadas para permisos, este cambio no debería dañarla. Esto es así especialmente si tu app no supone que se retiene el acceso de URI, almacena el estado de los permisos del sistema o actualiza el conjunto de imágenes que se muestran después de que cambia el permiso. Sin embargo, este comportamiento podría no ser ideal según el caso de uso de tu app. Para brindar la mejor experiencia a los usuarios, te recomendamos que implementes el selector de fotos o adaptes el selector de galería de tu app para controlar este comportamiento directamente con el permiso READ_MEDIA_VISUAL_USER_SELECTED.