Codename One añade un selector de contactos sin pedir acceso a la agenda
El PR #5680 introduce ContactPicker, una API que devuelve solo los contactos y campos que el usuario elige en la interfaz del sistema, sin permiso de lectura amplia.

Codename One ha integrado el PR #5680, que añade ContactPicker, una API para que una aplicación deje al usuario elegir uno o varios contactos concretos sin pedir permiso de lectura sobre toda la agenda. El framework es open source y compila una misma base de código en Java o Kotlin a aplicaciones nativas de iOS, Android, escritorio y web.
Cómo funciona
El desarrollador declara qué campos necesita y abre el selector del sistema:
ContactPicker picker = new ContactPicker();
picker.setRequestedFields(ContactPicker.NAME | ContactPicker.PHONE);
picker.pick(event -> {
Contact[] selected = ContactPicker.getPickedContacts(event);
if (selected.length == 0) { return; }
Contact contact = selected[0];
recipientName.setText(contact.getDisplayName());
recipientPhone.setText(contact.getPrimaryPhoneNumber());
});
Los campos disponibles incluyen nombre, teléfono, correo, dirección, foto, cumpleaños y sitio web. Se trata de una petición, no de una garantía: la plataforma puede devolver menos porque el contacto no tenga ese dato o porque su selector no soporte la combinación. El objeto Contact que llega es una instantánea, así que conviene copiar los valores que la pantalla vaya a usar mientras se gestiona el resultado. Su identificador no sirve después como token para leer la agenda a través de ContactsManager.
En Android 17 se usa ACTION_PICK_CONTACTS, la superficie que Google diseñó precisamente con la privacidad en mente. En versiones anteriores el fallback es ACTION_PICK para un solo contacto y sin solicitar el permiso amplio, aunque ahí no se puede prometer ni la selección múltiple ni todos los campos pedidos. En iOS se presenta CNContactPickerViewController y no hace falta NSContactsUsageDescription, porque el usuario elige dentro de una interfaz que controla el sistema. El simulador ofrece un selector determinista para probar la interfaz y la cancelación. Otros ports declaran la capacidad como no soportada.
La recomendación es mantener siempre la entrada manual: consultar ContactPicker.isSupported() y, si devuelve falso, ocultar el botón de elegir contacto y mostrar el formulario. Un teléfono ausente es un resultado normal, no un error.
El detalle que importa
El cambio no está solo en la API. El builder antes trataba cualquier referencia cercana a la API de contactos como motivo para añadir acceso amplio a la agenda, lo que habría dejado sin sentido al selector. Ahora el escaneo de características distingue entre ContactPicker, los objetos de valor de contacto y las llamadas directas al lector amplio. Usar Contact como resultado no arrastra el permiso por asociación; llamar a un método de Display que lee la agenda, sí. getDisplayName() puede sintetizar una etiqueta útil cuando el contacto nativo no tiene nombre visible, y cuando la distinción entre nombre y apellido importa hay que tirar de esos campos concretos.
La misma línea siguen el soporte de OTP de esta semana, que acepta un código sin leer la bandeja de entrada, y los paquetes de llamadas y VPN, que activan únicamente sus propios servicios nativos.
El PR está ya en el repositorio y la documentación del framework sigue en su web. Para quien hace aplicaciones móviles, la consecuencia práctica es que el camino con menos permisos resulta también el más simple: declarar el campo que la pantalla necesita, abrir el selector del sistema y ahorrarse la pantalla de explicación previa, la recuperación de la denegación y el desvío a los ajustes con la esperanza de que el usuario acabe concediendo acceso a su lista completa de conocidos.
