Novedades de productos

Una forma más confiable de agregar y editar contactos en Android

Lectura de 6 min
Ver el perfil de Roxanna Aliabadi Walker
Roxanna Aliabadi Walker Gerente de producto, Android

¿Qué cambiará?

Las apps que se orienten a la próxima versión de Android ya no podrán escribir directamente en los contactos con el permiso WRITE_CONTACTS. En su lugar, puedes usar los intents de contacto estándar (ACTION_INSERT, ACTION_EDIT y ACTION_INSERT_OR_EDIT). Para obtener una implementación de referencia completa, consulta la guía para desarrolladores sobre cómo modificar contactos con intents. 

En los dispositivos que ejecuten la próxima versión de Android, estos tres intents de contactos se comportarán de manera coherente y no necesitarán ningún permiso. Si tu app se orienta a la próxima versión de Android o una posterior, y trata de escribir en los contactos directamente, esas operaciones de escritura se rechazarán y generarán una SecurityException o una denegación de permiso. Para el pequeño conjunto de apps que requieren acceso amplio para crear y modificar contactos, se incluye un nuevo permiso especial de acceso a la app, MANAGE_CONTACTS, en Android 17 QPR2 (4º trimestre de 2026). Hoy, Google Play publicará lineamientos actualizados para el acceso a los contactos, incluido MANAGE_CONTACTS.

Por qué hacemos esto

Actualmente, si tu app necesita guardar incluso un solo contacto, es posible que solicites WRITE_CONTACTS. Sin embargo, ese permiso otorga un acceso amplio, lo que permite que una app realice cambios generalizados en los contactos de todo el dispositivo en segundo plano, incluso sin la interacción del usuario. La mayoría de las apps no necesitan un permiso tan amplio.

Si bien existen alternativas, nos comentaron que deben estar más estandarizadas en todo el ecosistema. Anteriormente, los intents de contacto se comportaban de manera diferente de un dispositivo a otro. Algunos editores descartaron los datos que pasaste, como una foto de contacto. En algunos dispositivos, el botón Atrás guardaba el contacto sin que el usuario lo quisiera o la opción "Crear contacto nuevo" era difícil de encontrar. Por lo tanto, pedir permiso era la opción racional. Estamos corrigiendo todos estos problemas en la próxima versión de Android y exigiendo que los dispositivos que la ejecuten sigan los mismos estándares.

Qué usar en su lugar

Cómo agregar o editar un contacto: Usa los intents de contacto

Cuando usas estos intents (ACTION_INSERT, ACTION_EDIT y ACTION_INSERT_OR_EDIT), el sistema se encarga del trabajo pesado y presenta una experiencia de edición de contactos limpia y estandarizada. Se abre un formulario rápido de media hoja para las entradas nuevas o permite que el usuario busque y agregue información fácilmente a un contacto existente. Los usuarios pueden expandir esto sin problemas a un editor de página completa para completar más detalles, todo sin que tu app necesite permisos de escritura directos. Consulta las simulaciones de UX tentativas a continuación: 

Figure01_01_Quick_Insert_Half_Sheet_Prefilled.png
Captura de pantalla 2 - Título: Maqueta de hoja completa de Crear un contacto nuevo: Captura de pantalla de una interfaz de usuario de Android tentativa que muestra un editor de contactos de pantalla completa con un selector de avatar, nombre, teléfono y campos de correo electrónico. El diseño está etiquetado como "UX not Final".

Figura 1: Crea un contacto nuevo  *La UX no es definitiva*

Figure02_01_Add_To_Existing_0_Search_State_Half_Sheet.png
Captura de pantalla 2 - Título: Maqueta de hoja completa de "Agregar información a un contacto existente": Captura de pantalla de una interfaz de usuario de Android tentativa que muestra un editor de contactos de pantalla completa con un selector de avatar y campos de nombre, teléfono y correo electrónico. El diseño de página completa incluye chips de divulgación progresiva para los campos secundarios. El diseño está etiquetado como "UX not Final".

Figura 2: Agrega información a un contacto existente *UX no final*


Nota: Es posible que los diferentes fabricantes de dispositivos modifiquen la experiencia del usuario para que coincida con el estilo de su SO, pero el comportamiento seguirá siendo el mismo.


Puedes optimizar aún más el proceso de creación pasando cargas útiles de datos opcionales, conocidas como extras, junto con tu intent:

  • EXTRA_ACCOUNT sugiere en qué cuenta se guarda el contacto, lo que resulta útil si tu app sincroniza contactos. Este extra ya existía, pero la compatibilidad variaba según el dispositivo. A partir de la próxima versión de Android, la compatibilidad se estandarizará en todos los dispositivos.
  • EXTRA_PHOTO_URI pasa una foto para precompletar la imagen del contacto. El editor controla el permiso de lectura del URI por ti. Esta es una novedad de la próxima versión de Android y será compatible con todo el ecosistema.

Antes: Escritura directa con WRITE_CONTACTS (Se genera una SecurityException o una denegación de permiso una vez que tu app se orienta a la próxima versión de Android.)

 

// Requires android.permission.WRITE_CONTACTS
fun saveContactDirectly(context: Context, name: String, phone: String, account: Account) {
    val ops = arrayListOf(
        ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type)
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, account.name)
            .build(),
        ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(
                ContactsContract.Data.MIMETYPE,
                ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
            )
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
            .build(),
        ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(
                ContactsContract.Data.MIMETYPE,
                ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
            )
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .build()
    )
    context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
}

 

 

Después: Se abre el editor de contactos (No se necesita permiso.)

 

fun saveContactWithEditor(
    context: Context,
    name: String,
    phone: String,
    account: Account,
    photoUri: Uri
) {
    val intent = Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI).apply {
        putExtra(ContactsContract.Intents.Insert.NAME, name)
        putExtra(ContactsContract.Intents.Insert.PHONE, phone)
        // Suggest which account the contact is saved to
        putExtra(ContactsContract.Intents.Insert.EXTRA_ACCOUNT, account)
        // New in the upcoming Android release: pre-fill the contact photo
        putExtra(ContactsContract.Intents.Insert.EXTRA_PHOTO_URI, photoUri)
        clipData = ClipData.newRawUri("", photoUri)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    context.startActivity(intent)
}

 

Antes: Actualizar un contacto existente directamente

 

// Requires android.permission.WRITE_CONTACTS
fun addPhoneDirectly(context: Context, rawContactId: Long, phone: String) {
    val values = ContentValues().apply {
        put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
        put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
        put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
    }
    context.contentResolver.insert(ContactsContract.Data.CONTENT_URI, values)
}

Después: Permitir que el usuario elija un contacto para actualizar o crear uno nuevo

fun addPhoneWithEditor(context: Context, phone: String) {
    val intent = Intent(Intent.ACTION_INSERT_OR_EDIT).apply {
        type = ContactsContract.Contacts.CONTENT_ITEM_TYPE
        putExtra(ContactsContract.Intents.Insert.PHONE, phone)
    }
    context.startActivity(intent)
}

Si ya sabes qué contacto actualizar, usa ACTION_EDIT con el URI de ese contacto:

fun editKnownContact(context: Context, contactUri: Uri, phone: String) {
    val intent = Intent(Intent.ACTION_EDIT, contactUri).apply {
        putExtra(ContactsContract.Intents.Insert.PHONE, phone)
    }
    context.startActivity(intent)
}

 

Cómo elegir un contacto: Usa el Selector de contactos

Si solo necesitas leer los contactos que elige el usuario, usa el Selector de contactos, disponible desde Android 17. No necesita ningún permiso.

Apps de llamadas y mensajería: Usa el Directorio de personas

Algunas apps agregan acciones como "Llamar con…" o "Enviar mensaje con…" a los contactos para que las personas puedan iniciar una llamada o un chat desde la app de Teléfono o Contactos. Si tu app hace esto hoy escribiendo entradas adicionales en los contactos del usuario, migra al Directorio de personas, que llegará en Android QPR2 (4º trimestre de 2026). Es un nuevo directorio del sistema en el que tu app publica cómo las personas pueden comunicarse entre sí en tu app, sin editar los contactos del usuario.

Ejemplos de cómo pueden migrar las apps

Para ver cómo funcionan estas alternativas en la práctica, aquí tienes algunos ejemplos de cómo diferentes tipos de apps pueden abordar el acceso a los contactos:

Ejemplo de tipo de app (no exclusivo).Ruta de migración recomendada
Apps de redes sociales, pagos P2P y viajes compartidosSi tu app permite que los usuarios encuentren amigos, dividan una cuenta o compartan el estado de un viaje, no necesitas permisos para acceder a los contactos. Usa el selector de contactos para seleccionar un contacto una sola vez y, luego, inicia ACTION_INSERT con EXTRA_PHOTO_URI cuando un usuario presione "Guardar en Contactos" en un perfil.
Apps de escáneres de tarjetas de presentación y de redes de eventosSi tu app escanea un pase o un código QR y guarda un contacto nuevo, o agrega un número de teléfono laboral a la entrada de un colega existente, usa ACTION_INSERT o ACTION_INSERT_OR_EDIT. El editor estandarizado de media hoja precompleta los campos analizados (nombre, empresa, teléfono, correo electrónico y foto) para que el usuario pueda verificar y guardar con un solo toque sin otorgar permisos en segundo plano.
Apps de mensajería y llamadas de VoIPEn lugar de solicitar que WRITE_CONTACTS inserte filas personalizadas de "Llamar con…" o "Enviar mensaje con…" en la libreta de direcciones del usuario, publica metadatos de accesibilidad a través del Directorio de personas y usa ACTION_INSERT cuando los usuarios agreguen un contacto nuevo desde un hilo de chat.
Apps de CRM empresarial y sincronización en la nubeSi tu app realiza una sincronización continua y bidireccional de contactos en segundo plano con una cuenta empresarial o de nube, en la que no es posible mostrarle al usuario una IU de intent para cada registro, tu app cumple con los requisitos para declarar MANAGE_CONTACTS .

Cambios que se deben verificar

  • No se admiten los tipos de datos personalizados en el editor. Si pasas filas Insert.DATA con tipos de MIME que no sean los estándar ContactsContract.CommonDataKinds, el editor ignorará esos tipos de MIME de forma silenciosa.
  • SHOW_OR_CREATE_CONTACT no se controla con el editor del sistema. Las apps de contactos que se incluyen con el dispositivo aún pueden controlarlo. Para obtener un comportamiento coherente en todos los dispositivos, usa ACTION_INSERT_OR_EDIT.
  • Las operaciones de escritura fallan de forma predeterminada. Una vez que te orientes a la próxima versión de Android, cualquier escritura directa sin MANAGE_CONTACTS generará una SecurityException or permission denial. Realiza pruebas para detectarlo.

¿Necesito MANAGE_CONTACTS?

Probablemente no. Si tu app guarda o edita contactos porque un usuario se lo pidió, los intents anteriores te cubren, sin permiso ni declaración de Play Console.

MANAGE_CONTACTS es para apps cuyo propósito principal es administrar contactos, en las que los intents no son suficientes. Por ejemplo:

Tu app…¿Necesitas MANAGE_CONTACTS?
Guarda un contacto de un chat, una tarjeta de presentación o un pedido.No, usa ACTION_INSERT
Agrega un número o un correo electrónico a un contacto existente.No, usa ACTION_INSERT_OR_EDIT o ACTION_EDIT
Permite que los usuarios elijan contactos para invitarlos, compartir contenido con ellos o pagarles.No, usa el Selector de contactos
Agrega acciones de llamada o mensaje a los contactos.No, usar el Directorio de personas
Tiene su propia pantalla de edición de contactos con su marca.No, una interfaz personalizada por sí sola no cumple con los requisitos.
Es un administrador de contactos dedicado o una app de reemplazo del marcador.Es probable que sí
Es una app de protección contra spam y de identificación de llamadas que actualiza de forma masiva las entradas de la libreta de direcciones.Es probable que sí
Sincroniza contactos con una cuenta empresarial o de nube en segundo plano.Es probable que sí
Realiza copias de seguridad y restablecimientos de contactos de forma masiva, o bien transferencias de teléfono de un dispositivo a otro.Es probable que sí
Establece tonos personalizados para contactos individuales.Es probable que sí

Cómo funciona:

  • MANAGE_CONTACTS es un permiso de acceso especial para apps. No es un diálogo de tiempo de ejecución. Los usuarios pueden activarla en Configuración > Acceso especial de apps > Administrar contactos.
// Opens Settings > Apps > Special app access > Manage contacts
val intent = Intent(Settings.ACTION_MANAGE_CONTACTS, Uri.fromParts("package", packageName, null))
context.startActivity(intent)
  • Abarca la lectura, la adición, la edición y la eliminación de contactos, cuentas y datos del Directorio de personas. Tampoco es necesario que solicites READ_CONTACTS.
  • Para publicar en Google Play con MANAGE_CONTACTS, debes enviar una declaración en Play Console en la que expliques por qué los intents no son suficientes para la funcionalidad principal de tu app.

Pasos de la migración

  • Busca las escrituras de contacto. Busca WRITE_CONTACTS, ContactsContract.RawContacts.CONTENT_URI, ContactsContract.Data.CONTENT_URI y applyBatch(ContactsContract.AUTHORITY, …) en tu código.
  • Reemplaza cada escritura controlada por el usuario por un intent: ACTION_INSERT para contactos nuevos, y ACTION_EDIT o ACTION_INSERT_OR_EDIT para actualizaciones.
  • Usa solo tipos de datos estándar en Insert.DATA. Se movieron las acciones de llamada o mensaje al directorio de personas.
  • Decide si realmente necesitas MANAGE_CONTACTS con la tabla anterior. Si es así, planifica tu declaración de Play Console.
  • Quita WRITE_CONTACTS de tu manifiesto una vez que ninguna ruta de código lo necesite.

Cronograma

CuándoQué ocurre
21 de octubre de 2026Google Play anuncia la política de MANAGE_CONTACTS
2026 (versión trimestral de la plataforma 2 de Android 17)El permiso MANAGE_CONTACTS pasa a estar disponible
2027 (próxima versión de Android)WRITE_CONTACTS ya no otorga acceso de escritura a las apps que se orientan a la próxima versión de Android. Los intents de contacto están estandarizados en todos los dispositivos
2027, no antes de la próxima versión de AndroidPlay Console solicita a las apps que usan MANAGE_CONTACTS que envíen una declaración
2027, no antes de la próxima versión de AndroidSe requiere el cumplimiento de las políticas de Play

Preguntas frecuentes

  • ¿Esto afecta a todas las apps?
    • El cambio de baja de WRITE_CONTACTS solo se aplica a las apps que se orientan a la próxima versión de Android y posteriores. Las apps orientadas a versiones anteriores conservarán su comportamiento actual. Sin embargo, el nuevo editor de contactos estandarizado estará disponible para todas las apps que se ejecuten en el nuevo SO, independientemente de su SDK de destino.
  • ¿Mi app puede borrar contactos con un intent?
    • No. No hay ningún intent para borrar contactos. Los usuarios borran contactos en su app de contactos. Las apps con MANAGE_CONTACTS pueden borrar contactos directamente.
  • Ya tengo mi propia pantalla de edición de contactos. ¿Puedo quedármelo?
    • Solo si tu app cumple con los requisitos para MANAGE_CONTACTS. Querer mantener un diseño personalizado no es un motivo válido por sí solo.
  • ¿MANAGE_CONTACTS reemplaza READ_CONTACTS?
    • MANAGE_CONTACTS incluye acceso de lectura, por lo que no es necesario que solicites ambos, pero muchas apps que no necesitan modificar los contactos califican para READ_CONTACTS. Estas apps pueden seguir solicitando READ_CONTACTS.
  • ¿Qué sucede con mis usuarios que tienen versiones anteriores de Android?
    • Los intents de contacto también funcionan en versiones anteriores, administrados por la app de contactos del dispositivo. La mayoría de las apps funcionarán bien en todos los dispositivos que usen estos intents. Si tu app se ve afectada por un problema de coherencia específico que solo se resuelve en las versiones más recientes, puedes mantener el permiso WRITE_CONTACTS en tu manifiesto con un maxSdkVersion adecuado.
Escrito por:
Continuar leyendo