Novedades de productos
Selector de contactos: Uso compartido de contactos con prioridad para la privacidad
Lectura de 4 min
La privacidad y el control del usuario siguen siendo el centro de la experiencia de Android. Así como el selector de fotos hizo que el uso compartido de contenido multimedia fuera seguro y fácil de implementar, ahora brindamos ese mismo nivel de privacidad, simplicidad y excelente experiencia del usuario a la selección de contactos.
Un nuevo estándar para la privacidad de los contactos
Históricamente, las aplicaciones que requerían acceso a los contactos de un usuario específico dependían del permiso amplio READ_CONTACTS. Si bien es funcional, este enfoque a menudo otorgaba a las apps más datos de los necesarios. El nuevo selector de contactos de Android, que se introdujo en Android 17, cambia esta dinámica al proporcionar una interfaz estandarizada, segura y con capacidad de búsqueda para la selección de contactos.
Esta función permite a los usuarios otorgar a las apps acceso solo a los contactos específicos que elijan, lo que se alinea con el compromiso de Android con la transparencia de los datos y las huellas de permisos minimizadas.
Cómo funciona
Los desarrolladores pueden integrar el selector de contactos con el intent Intent.ACTION_PICK_CONTACTS. Esta API actualizada ofrece varias capacidades potentes:
- Solicitudes de datos detalladas: Las apps pueden especificar exactamente qué campos necesitan, como números de teléfono o direcciones de correo electrónico, en lugar de recibir el registro de contacto completo.
- Compatibilidad con la selección múltiple: El selector admite selecciones de contactos únicas y múltiples, lo que brinda a los desarrolladores más flexibilidad para funciones como las invitaciones grupales.
- Límites de selección: Los desarrolladores pueden establecer límites personalizados en la cantidad de contactos que un usuario puede seleccionar a la vez.
- Acceso temporal: Cuando se selecciona, el sistema muestra un URI de sesión que proporciona acceso de lectura temporal a los datos solicitados, lo que garantiza que el acceso no persista más de lo necesario.
- Acceso a otros perfiles: Cuando se usa este nuevo intent, la interfaz permitirá a los usuarios seleccionar contenido de otros perfiles de usuario, como un perfil de trabajo, un perfil clonado o un espacio privado.
- Rendimiento optimizado: El selector de contactos muestra un solo URI que permite realizar consultas colectivas de resultados, lo que elimina la necesidad de consultar el URI de contacto individual por separado, como lo requiere
ACTION_PICK. Esta eficiencia reduce aún más la sobrecarga del sistema mediante el uso de una sola transacciónBinder.
Retrocompatibilidad e implementación
En los dispositivos que ejecutan Android 17 o versiones posteriores, el sistema actualiza automáticamente los intents ACTION_PICK heredados que especifican tipos de datos de contacto a la interfaz nueva y más segura. Sin embargo, para aprovechar al máximo las funciones avanzadas, como la selección múltiple, se recomienda a los desarrolladores que actualicen su código de implementación y utilicen ContentResolver para consultar el URI de sesión que se muestra.
Integra el selector de contactos. Para integrar el selector de contactos, los desarrolladores usan el intent ACTION_PICK_CONTACTS. A continuación, se muestra un ejemplo de código que demuestra cómo iniciar el selector y solicitar campos de datos específicos, como el correo electrónico y los números de teléfono.
// State to hold the list of selected contacts var contacts by remember { mutableStateOf<List>(emptyList()) } // Launcher for the Contact Picker intent val pickContact = rememberLauncherForActivityResult(StartActivityForResult()) { if (it.resultCode == Activity.RESULT_OK) { val resultUri = it.data?.data ?: return@rememberLauncherForActivityResult // Process the result URI in a background thread coroutine.launch { contacts = processContactPickerResultUri(resultUri, context) } } } // Define the specific contact data fields you need val requestedFields = arrayListOf( Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE, ) // Set up the intent for the Contact Picker val pickContactIntent = Intent(ACTION_PICK_CONTACTS).apply { putExtra(EXTRA_PICK_CONTACTS_SELECTION_LIMIT, 5) putStringArrayListExtra( EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS, requestedFields ) putExtra(EXTRA_PICK_CONTACTS_MATCH_ALL_DATA_FIELDS, false) } // Launch the picker pickContact.launch(pickContactIntent)
Después de que el usuario realiza una selección, la app procesa el resultado consultando el URI de sesión que se muestra para extraer la información de contacto solicitada.
// Data class representing a parsed Contact with selected details data class Contact(val id: String, val name: String, val email: String?, val phone: String?) // Helper function to query the content resolver with the URI returned by the Contact Picker. // Parses the cursor to extract contact details such as name, email, and phone number private suspend fun processContactPickerResultUri( sessionUri: Uri, context: Context ): List<Contact> = withContext(Dispatchers.IO) { // Define the columns we want to retrieve from the ContactPicker ContentProvider val projection = arrayOf( ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, ContactsContract.Data.MIMETYPE, // Type of data (e.g., email or phone) ContactsContract.Data.DATA1, // The actual data (Phone number / Email string) ) val results = mutableListOf<Contact>() // Note: The Contact Picker Session Uri doesn't support custom selection & selectionArgs. context.contentResolver.query(sessionUri, projection, null, null, null)?.use { cursor -> // Get the column indices for our requested projection val contactIdIdx = cursor.getColumnIndex(ContactsContract.Contacts._ID) val mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE) val nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY) val data1Idx = cursor.getColumnIndex(ContactsContract.Data.DATA1) while (cursor.moveToNext()) { val contactId = cursor.getString(contactIdIdx) val mimeType = cursor.getString(mimeTypeIdx) val name = cursor.getString(nameIdx) ?: "" val data1 = cursor.getString(data1Idx) ?: "" // Determine if the current row represents an email or a phone number val email = if (mimeType == Email.CONTENT_ITEM_TYPE) data1 else null val phone = if (mimeType == Phone.CONTENT_ITEM_TYPE) data1 else null // Add the parsed contact to our results list results.add(Contact(contactId, name, email, phone)) } } return@withContext results }
Consulta la documentación completa aquí.
Prácticas recomendadas para desarrolladores
Para proporcionar la mejor experiencia del usuario y mantener altos estándares de seguridad, te recomendamos lo siguiente:
- Minimización de datos: Solicita solo los campos de datos específicos (p.ej., correo electrónico) que necesita tu app.
- Persistencia inmediata: Conserva los datos seleccionados de inmediato, ya que el acceso al URI de sesión es temporal.
Seguir leyendo
-
Novedades de productos
El selector de fotos incorporado: Una forma más fluida de solicitar fotos y videos de forma privada en tu app.
Roxanna Aliabadi Walker, Yacine Rezgui • Lectura de 8 min
-
Novedades de productos
Como se anunció hoy durante The Android Show, Android está pasando de un sistema operativo a un sistema de inteligencia, lo que crea más oportunidades de interacción con tus apps.
Matthew McCullough • Lectura de 4 min
-
Novedades de productos
El ecosistema móvil siempre está evolucionando, lo que genera nuevas oportunidades y nuevas amenazas. A través de estos cambios, Android y Google Play siguen comprometidos a garantizar que miles de millones de usuarios puedan seguir disfrutando de sus apps con confianza y que la innovación de los desarrolladores pueda prosperar.
Vijaya Kaza • Lectura de 3 min
Mantente al día
Recibe la información más reciente sobre el desarrollo de Android en tu bandeja de entrada todas las semanas.