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: Conserva 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 Google Play, nos comprometemos a brindar la mejor experiencia posible a los usuarios y, al mismo tiempo, garantizar que los desarrolladores tengan las herramientas y la adaptabilidad necesarias para alcanzar el éxito.
Paul Feng • Lectura de 3 min -
Novedades sobre productosEl año pasado, presentamos la verificación de desarrolladores de Android para fortalecer la seguridad del ecosistema y evitar que los agentes maliciosos se oculten detrás del anonimato para lanzar apps dañinas.
Matthew Forsythe • Lectura de 2 min
Recibe la información más reciente sobre el desarrollo de Android en tu bandeja de entrada todas las semanas.