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 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 implementarlo 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 con 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 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 Fotos seleccionado | Comportamiento de la UX |
---|---|---|---|
SDK 33 | No | No | N/A |
Sí | Sí | Controlado por la app | |
SDK 34 | No | Sí | Controlado por el sistema (comportamiento de compatibilidad) |
Sí | Sí | Controlado por la app |
Crea o adapta tu propio selector de galería
Crear tu propio selector de galería requiere un desarrollo y un 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 se orienta a Android 14 (nivel de API 34) o una versión posterior, 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 nuevas opciones.
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 mantener 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, también 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 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:
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 de onStart
y onResume
, ya que el usuario puede cambiar 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
}
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 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.
Cómo consultar la última selección
Las apps en Android 15 y versiones posteriores, y en Android 14 con compatibilidad con las actualizaciones del sistema de Google Play, pueden consultar la última selección de imágenes y videos que hizo el usuario en el 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:
- Tu app se instaló en un dispositivo que ejecuta Android 13.
- El usuario otorgó los permisos
READ_MEDIA_IMAGES
yREAD_MEDIA_VIDEO
a tu app. - Luego, el dispositivo se actualiza a Android 14 mientras la app está instalada.
- 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:
- Tu app se instaló en un dispositivo que ejecuta Android 13.
- El usuario otorgó los permisos
READ_EXTERNAL_STORAGE
oWRITE_EXTERNAL_STORAGE
a tu app. - Luego, el dispositivo se actualiza a Android 14 mientras la app está instalada.
- 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 que inicie 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
conContentResolver
, 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
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 el tipo de contenido multimedia seleccionable por permiso
El diálogo de selección es sensible al tipo de permiso solicitado:
- Si solo solicitas
READ_MEDIA_IMAGES
, solo se mostrarán las imágenes que se pueden seleccionar. - Si solo solicitas
READ_MEDIA_VIDEO
, solo se mostrará el video que se puede seleccionar. - Si solicitas
READ_MEDIA_IMAGES
yREAD_MEDIA_VIDEO
, se muestra toda la biblioteca de fotos para que se pueda seleccionar.
Según los casos de uso de tu app, debes asegurarte de solicitar los permisos correctos para evitar una mala experiencia del usuario. 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.
Permite 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 completo a acceso parcial a través de la configuración de permisos sin otorgar acceso a algunos archivos multimedia 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 brinda una concesión de 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 cualquier otro permiso único.
Comportamiento durante la nueva selecció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 de permisos, este cambio no debería dañarla. Esto es especialmente cierto si tu app no supone que se retiene el acceso de 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, es posible que este comportamiento no sea ideal según el caso de uso de tu app. Para ofrecer la mejor experiencia a los usuarios, te recomendamos que implementes el selector de fotos o que adaptes el selector de galería de tu app para controlar este comportamiento directamente con el permiso READ_MEDIA_VISUAL_USER_SELECTED
.