Selector de contactos: Compartir contactos con prioridad en la privacidad
Lectura de 4 min
La privacidad y el control del usuario siguen siendo el eje central 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 el mismo nivel de privacidad, simplicidad y excelente experiencia del usuario a la selección de contactos.
Un nuevo estándar para la privacidad de la información de contacto
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 este enfoque era funcional, 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, ya que proporciona una interfaz estandarizada, segura y con capacidad de búsqueda para la selección de contactos.
Esta función permite que los usuarios otorguen acceso a las apps solo a los contactos específicos que elijan, lo que se alinea con el compromiso de Android con la transparencia de los datos y la minimización de los permisos.
Cómo funciona
Los desarrolladores pueden integrar el Selector de contactos con el intent Intent.ACTION_PICK_CONTACTS. Esta API actualizada ofrece varias funciones 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 la selección de uno o varios contactos, lo que brinda a los desarrolladores más flexibilidad para funciones como las invitaciones a grupos.
- 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 esta opción, el sistema devuelve 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 esta nueva intención, la interfaz permitirá que los usuarios seleccionen contenido de otros perfiles de usuario, como un perfil de trabajo, un perfil clonado o un espacio privado.
- Rendimiento optimizado: El selector de contactos devuelve un solo URI que permite consultar resultados colectivos, lo que elimina la necesidad de consultar el URI de cada contacto por separado, como lo requiere
ACTION_PICK. Esta eficiencia reduce aún más la sobrecarga del sistema, ya que utiliza una sola transacción deBinder.
Retrocompatibilidad y su implementación
En el caso de 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 nueva interfaz 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 devuelto.
Integra el selector de contactosPara 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 direcciones de correo electrónico y 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 devuelto 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., el correo electrónico) que necesita tu app.
- Persistencia inmediata: Persiste los datos seleccionados de inmediato, ya que el acceso al URI de sesión es temporal.
-
Novedades sobre productosSelector de fotos integrado: 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 sobre productosEn marzo, presentamos Android Bench, nuestra tabla de clasificación de LLM para tareas de desarrollo de Android del mundo real. Desde entonces, mejoramos la comparativa según tus comentarios, lo que incluye la evaluación de modelos de peso abierto y la incorporación de dimensiones de costo y eficiencia al ranking.
Zoe Lopez-Latorre • Lectura de 3 minutos -
Novedades sobre productosEn Google Play, nos comprometemos a ofrecer la mejor experiencia posible a los usuarios, a la vez que garantizamos que los desarrolladores tengan las herramientas y la adaptabilidad necesarias para triunfar.
Paul Feng • Lectura de 3 min
Recibe la información más reciente sobre el desarrollo de Android en tu bandeja de entrada todas las semanas.