Cómo otorgar acceso parcial a fotos y videos

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

Este cambio solo está habilitado 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 de selección de imágenes y videos que también mejore la privacidad del usuario sin tener que solicitar ningún permiso de almacenamiento.

Si usas tu propio selector de galería con permisos de almacenamiento y necesitas tener el control total de tu implementación, adapta tu implementación para usar el nuevo permiso READ_MEDIA_VISUAL_USER_SELECTED. Si tu app no usa el nuevo permiso, 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 compatible)
Controlado por la app

La creación de tu propio selector de galería 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 la app 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 cómo solicitar permisos y seleccionar contenido multimedia con las opciones nuevas.

La extensión .
Figura 1: El nuevo diálogo le 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 denegarlo todo.

En esta sección, se muestra el enfoque recomendado para crear tu propio selector de galerías con MediaStore. Si ya mantienes un selector de galería para tu app y necesitas conservar el control total, puedes usar estos ejemplos para adaptar tu implementación. Si no actualizas tu implementación para controlar 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 configurar un maxSdkVersion 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 de Acceso a fotos seleccionados 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 para permitir que los usuarios le 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 le permite al usuario volver a seleccionar las fotos y los videos que desea que estén disponibles para tu app.

Cuando abras el diálogo de selección, se mostrarán 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 verificar 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 el otorgamiento de permisos se puede cambiar entre las devoluciones de llamada del ciclo de vida de la app onStart y onResume, ya que el usuario puede modificar el acceso en 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
}

Consulta la biblioteca del dispositivo

Una vez que hayas verificado 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
}

Se simplifica este fragmento de código 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.

Consultar la última selección

Las apps en Android 15 y versiones posteriores, y en Android 14 compatibles con las actualizaciones del sistema de Google Play pueden consultar la última selección de imágenes y videos que realizó el usuario con acceso parcial habilitando QUERY_ARG_LATEST_SELECTION_ONLY:

if (getExtensionVersion(Build.VERSION_CODES.U) >= 12) {
    val queryArgs = bundleOf(
        QUERY_ARG_SQL_SORT_ORDER to "${Images.Media.DATE_ADDED} DESC"
        QUERY_ARG_LATEST_SELECTION_ONLY to true
    )

    contentResolver.query(collectionUri, projection, queryArgs, null)
}

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 sobre 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 onResume, ya que el usuario podría cambiar de acceso completo a acceso parcial a través de 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, tu app tendrá acceso a las fotos y los videos seleccionados en algún momento. Tu app siempre debe controlar el caso de no tener acceso a ningún Uri, sin importar su autoridad.

Filtra los tipos de medios seleccionables por permiso

El diálogo de selección es sensible al tipo de permiso solicitado:

  • La solicitud de READ_MEDIA_IMAGES solo muestra las imágenes que se pueden seleccionar.
  • La solicitud solo de READ_MEDIA_VIDEO muestra que solo se puede seleccionar el video.
  • Cuando se solicitan READ_MEDIA_IMAGES y READ_MEDIA_VIDEO, se muestra que toda la biblioteca de fotos 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.

Cómo solicitar 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, tu 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 de acceso total a 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 inicial de contenido multimedia

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 permiso temporal y acceso temporal a las fotos y los videos que selecciona 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 similar a otros permisos únicos.

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 en 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 sobre permisos, este cambio no debería dañarla. Esto es especialmente cierto si tu app no supone que se retiene el acceso al URI, almacena el estado del permiso 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 la galería de la app para controlar este comportamiento directamente con el permiso READ_MEDIA_VISUAL_USER_SELECTED.