Cómo modificar contactos con intents

En esta lección, se muestra cómo usar un Intent para insertar un nuevo contacto o modificar los datos de uno existente. En lugar de acceder al Proveedor de contactos directamente, un Intent inicia la App de Contactos, que ejecuta la Activity apropiada. En el caso de las acciones de modificación que se describen en esta lección, si envías datos extendidos en el Intent, estos se ingresan en la IU de la Activity que se inicia.

Usar un Intent para insertar o actualizar un solo contacto es el método preferido para modificar el Proveedor de contactos. A continuación, se enumeran los motivos:

  • Te ahorra el tiempo y el esfuerzo que implica desarrollar una IU y un código propios.
  • Evita que las modificaciones que no cumplen con las reglas del Proveedor de contactos generen errores.
  • Reduce la cantidad de permisos que debes solicitar. Tu app no necesita permiso para escribir en el Proveedor de contactos, ya que este delega las modificaciones a la App de Contactos, que ya cuenta con este permiso.

Cómo insertar un nuevo contacto con un intent

A menudo quieres permitir que el usuario inserte un nuevo contacto cuando tu app recibe datos nuevos. Por ejemplo, una app de reseñas de restaurantes puede permitir a los usuarios agregar el restaurante como contacto cuando realizan la reseña. Para hacerlo mediante un intent, crea el intent con la mayor cantidad de datos que tengas y, luego, envíalo a la App de Contactos.

Cuando realizas la inserción mediante la App de Contactos, se agrega un nuevo contacto sin procesar en la tabla ContactsContract.RawContacts del Proveedor de contactos. Si es necesario, la App de Contactos solicita a los usuarios el tipo de cuenta y la cuenta que usarán cuando creen un contacto sin procesar. La App de Contactos también notifica a los usuarios si este ya existe. En este caso, los usuarios tienen la opción de cancelar la inserción, de modo que no se creará ningún contacto. Para obtener más información sobre los contactos sin procesar, consulta la guía de la API del Proveedor de Contactos.

Cómo crear un intent

Para comenzar, crea un nuevo objeto Intent con la acción Intents.Insert.ACTION. Configura el tipo de MIME en RawContacts.CONTENT_TYPE. Por ejemplo:

Kotlin

    ...
    // Creates a new Intent to insert a contact
    val intent = Intent(ContactsContract.Intents.Insert.ACTION).apply {
        // Sets the MIME type to match the Contacts Provider
        type = ContactsContract.RawContacts.CONTENT_TYPE
    }
    

Java

    ...
    // Creates a new Intent to insert a contact
    Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);
    // Sets the MIME type to match the Contacts Provider
    intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
    

Si ya tienes detalles del contacto, como el número de teléfono o la dirección de correo electrónico, puedes insertarlos en el intent como datos extendidos. Para obtener un valor clave, usa la constante apropiada de Intents.Insert. La App de Contactos muestra los datos en su pantalla de inserción, lo que permite a los usuarios modificar o agregar información.

Kotlin

    private var emailAddress: EditText? = null
    private var phoneNumber: EditText? = null
    ...
    /* Assumes EditText fields in your UI contain an email address
     * and a phone number.
     *
     */
    emailAddress = findViewById(R.id.email)
    phoneNumber = findViewById(R.id.phone)
    ...
    /*
     * Inserts new data into the Intent. This data is passed to the
     * contacts app's Insert screen
     */
    intent.apply {
        // Inserts an email address
        putExtra(ContactsContract.Intents.Insert.EMAIL, emailAddress?.text)
        /*
         * In this example, sets the email type to be a work email.
         * You can set other email types as necessary.
         */
        putExtra(
                ContactsContract.Intents.Insert.EMAIL_TYPE,
                ContactsContract.CommonDataKinds.Email.TYPE_WORK
        )
        // Inserts a phone number
        putExtra(ContactsContract.Intents.Insert.PHONE, phoneNumber?.text)
        /*
         * In this example, sets the phone type to be a work phone.
         * You can set other phone types as necessary.
         */
        putExtra(
                ContactsContract.Intents.Insert.PHONE_TYPE,
                ContactsContract.CommonDataKinds.Phone.TYPE_WORK
        )
    }
    

Java

    private EditText emailAddress = null;
    private EditText phoneNumber = null;
    ...
    /* Assumes EditText fields in your UI contain an email address
     * and a phone number.
     *
     */
    emailAddress = (EditText) findViewById(R.id.email);
    phoneNumber = (EditText) findViewById(R.id.phone);
    ...
    /*
     * Inserts new data into the Intent. This data is passed to the
     * contacts app's Insert screen
     */
    // Inserts an email address
    intent.putExtra(ContactsContract.Intents.Insert.EMAIL, emailAddress.getText())
    /*
     * In this example, sets the email type to be a work email.
     * You can set other email types as necessary.
     */
          .putExtra(ContactsContract.Intents.Insert.EMAIL_TYPE,
                ContactsContract.CommonDataKinds.Email.TYPE_WORK)
    // Inserts a phone number
          .putExtra(ContactsContract.Intents.Insert.PHONE, phoneNumber.getText())
    /*
     * In this example, sets the phone type to be a work phone.
     * You can set other phone types as necessary.
     */
          .putExtra(ContactsContract.Intents.Insert.PHONE_TYPE,
                ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
    

Una vez que crees el Intent, envíalo mediante una llamada a startActivity().

Kotlin

        /* Sends the Intent
         */
        startActivity(intent)
    

Java

        /* Sends the Intent
         */
        startActivity(intent);
    

Esta llamada abre una pantalla en la App de Contactos, que permite a los usuarios ingresar un contacto nuevo. El tipo y el nombre de la cuenta del contacto aparecerán en la parte superior de la pantalla. Una vez que los usuarios ingresen los datos y hagan clic en Listo, aparecerá la lista de contactos de la App de Contactos. Luego, los usuarios podrán volver a tu app al hacer clic en Atrás.

Cómo editar un contacto existente con un intent

Editar un contacto por medio de un Intent resulta útil si el usuario ya eligió un contacto de interés. Por ejemplo, una app que busca contactos que tienen direcciones postales, pero no incluyen un código postal, podría ofrecer al usuario la opción de buscar el código y agregarlo en el contacto.

Si quieres editar un contacto existente por medio de un intent, usa un procedimiento similar al que se usa para insertar uno. Crea un intent como se describe en la sección Cómo insertar un nuevo contacto con un intent, pero agrega el Contacts.CONTENT_LOOKUP_URI del contacto y el tipo MIME Contacts.CONTENT_ITEM_TYPE al intent. Si quieres editar el contacto con detalles que ya tienes, puedes agregarlos en los datos extendidos del intent. Ten en cuenta que algunas columnas de nombre no se pueden editar de esta manera; estas se enumeran en la sección de resumen de la referencia de la API para la clase ContactsContract.Contacts en el encabezado "Actualizar".

Por último, envía el intent. En respuesta, la App de Contactos muestra una pantalla de edición. Una vez que el usuario termine de editar y guarde los cambios, la app mostrará una lista de contactos. Asimismo, cuando haga clic en Atrás, se mostrará tu app.

Cómo crear el intent

Si quieres editar un contacto, llama a Intent(action) para crear un intent con la acción ACTION_EDIT. Luego, llama a setDataAndType() a fin de configurar el valor de datos del intent del Contacts.CONTENT_LOOKUP_URI del contacto y el tipo MIME en Contacts.CONTENT_ITEM_TYPE. Como una llamada a setType() sobrescribe el valor de datos actual del Intent, debes configurar los datos y el tipo de MIME a la vez.

Para obtener el Contacts.CONTENT_LOOKUP_URI de un contacto, llama a Contacts.getLookupUri(id, lookupkey) con los valores Contacts._ID y Contacts.LOOKUP_KEY del contacto como argumentos.

Nota: El valor LOOKUP_KEY de un contacto es el identificador que deberías usar para obtenerlo. Este se mantiene constante incluso si el proveedor cambia el ID de la fila del contacto a fin de administrar operaciones internas.

En el siguiente fragmento, se muestra cómo crear un intent:

Kotlin

        // The Cursor that contains the Contact row
        var mCursor: Cursor? = null
        // The index of the lookup key column in the cursor
        var lookupKeyIndex: Int = 0
        // The index of the contact's _ID value
        var idIndex: Int = 0
        // The lookup key from the Cursor
        var currentLookupKey: String? = null
        // The _ID value from the Cursor
        var currentId: Long = 0
        // A content URI pointing to the contact
        var selectedContactUri: Uri? = null
        ...
        /*
         * Once the user has selected a contact to edit,
         * this gets the contact's lookup key and _ID values from the
         * cursor and creates the necessary URI.
         */
        mCursor?.apply {
            // Gets the lookup key column index
            lookupKeyIndex = getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)
            // Gets the lookup key value
            currentLookupKey = getString(lookupKeyIndex)
            // Gets the _ID column index
            idIndex = getColumnIndex(ContactsContract.Contacts._ID)
            currentId = getLong(idIndex)
            selectedContactUri = ContactsContract.Contacts.getLookupUri(currentId, mCurrentLookupKey)
        }

        // Creates a new Intent to edit a contact
        val editIntent = Intent(Intent.ACTION_EDIT).apply {
            /*
             * Sets the contact URI to edit, and the data type that the
             * Intent must match
             */
            setDataAndType(selectedContactUri, ContactsContract.Contacts.CONTENT_ITEM_TYPE)
        }
    

Java

        // The Cursor that contains the Contact row
        public Cursor mCursor;
        // The index of the lookup key column in the cursor
        public int lookupKeyIndex;
        // The index of the contact's _ID value
        public int idIndex;
        // The lookup key from the Cursor
        public String currentLookupKey;
        // The _ID value from the Cursor
        public long currentId;
        // A content URI pointing to the contact
        Uri selectedContactUri;
        ...
        /*
         * Once the user has selected a contact to edit,
         * this gets the contact's lookup key and _ID values from the
         * cursor and creates the necessary URI.
         */
        // Gets the lookup key column index
        lookupKeyIndex = mCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
        // Gets the lookup key value
        currentLookupKey = mCursor.getString(lookupKeyIndex);
        // Gets the _ID column index
        idIndex = mCursor.getColumnIndex(ContactsContract.Contacts._ID);
        currentId = mCursor.getLong(idIndex);
        selectedContactUri =
                Contacts.getLookupUri(currentId, mCurrentLookupKey);
        ...
        // Creates a new Intent to edit a contact
        Intent editIntent = new Intent(Intent.ACTION_EDIT);
        /*
         * Sets the contact URI to edit, and the data type that the
         * Intent must match
         */
        editIntent.setDataAndType(selectedContactUri, ContactsContract.Contacts.CONTENT_ITEM_TYPE);
    

Cómo agregar la marca de navegación

En Android 4.0 (API nivel 14) y versiones posteriores, un problema en la App de Contactos deriva en una navegación incorrecta. Cuando tu app envía un intent a la App de Contactos, y los usuarios editan y guardan un contacto, al hacer clic en Atrás, verán la pantalla de la lista de contactos. Para volver a tu app, deberán hacer clic en Recientes y seleccionarla.

Para solucionar este problema en Android 4.0.3 (API nivel 15) y versiones posteriores, agrega la clave de datos extendidos finishActivityOnSaveCompleted al intent, con un valor de true. Las versiones de Android anteriores a la 4.0 aceptan esta clave, pero no tiene ningún efecto. Para configurar los datos extendidos, haz lo siguiente:

Kotlin

        // Sets the special extended data for navigation
        editIntent.putExtra("finishActivityOnSaveCompleted", true)
    

Java

        // Sets the special extended data for navigation
        editIntent.putExtra("finishActivityOnSaveCompleted", true);
    

Cómo agregar otros datos extendidos

Para agregar datos extendidos adicionales al Intent, llama a putExtra() según lo desees. Puedes agregar datos extendidos para campos de contactos comunes mediante valores clave especificados en Intents.Insert. Recuerda que no es posible modificar algunas columnas de la tabla ContactsContract.Contacts. Estas se enumeran en la sección de resumen de la referencia de la API para la clase ContactsContract.Contacts en el encabezado "Actualizar".

Cómo enviar el intent

Por último, debes enviar el intent que construiste. Por ejemplo:

Kotlin

        // Sends the Intent
        startActivity(editIntent)
    

Java

        // Sends the Intent
        startActivity(editIntent);
    

Cómo permitir que los usuarios elijan insertar o editar contactos con un intent

Para permitir que los usuarios elijan si quieren insertar un contacto o editar uno existente, envía un Intent con la acción ACTION_INSERT_OR_EDIT. Por ejemplo, una app cliente de correo electrónico podría permitir a los usuarios que agreguen una dirección de correo electrónico entrante a un nuevo contacto, o bien agregarla como dirección adicional de uno existente. Configura el tipo de MIME para este intent en Contacts.CONTENT_ITEM_TYPE, pero no establezcas el URI de datos.

Cuando envíes este intent, la App de Contactos mostrará una lista de contactos. Los usuarios pueden insertar un nuevo contacto o elegir uno existente y editarlo. Cualquier campo de datos extendidos que agregas al intent completa la pantalla que aparece. Puedes usar cualquier valor clave especificado en Intents.Insert. En el siguiente fragmento de código, se muestra cómo construir y enviar el intent:

Kotlin

        // Creates a new Intent to insert or edit a contact
        val intentInsertEdit = Intent(Intent.ACTION_INSERT_OR_EDIT).apply {
            // Sets the MIME type
            type = ContactsContract.Contacts.CONTENT_ITEM_TYPE
        }
        // Add code here to insert extended data, if desired

        // Sends the Intent with an request ID
        startActivity(intentInsertEdit)
    

Java

        // Creates a new Intent to insert or edit a contact
        Intent intentInsertEdit = new Intent(Intent.ACTION_INSERT_OR_EDIT);
        // Sets the MIME type
        intentInsertEdit.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
        // Add code here to insert extended data, if desired
        ...
        // Sends the Intent with an request ID
        startActivity(intentInsertEdit);