Windows Phone 8: Gestión de contactos

Hola a todos!

Hasta ahora, cualquier aplicación podía hacer uso del lanzador SaveContactTask para añadir un contacto al teléfono. Lo malo de este lanzador es que requiere intervención del usuario, por lo que está muy bien para guardar un contacto pero es inusable para guardar muchos más.

En Windows Phone 8 una de las novedades del sistema es el poder integrar aplicaciones VoIP con el sistema. Para ello estas aplicaciones pueden publicar la lista de contactos en el people hub. Pero esta característica no es única de las aplicaciones VoIP, cualquier aplicación puede crear su propia “Store” dentro del people hub y añadir contactos a ella de forma automática. Para ello haremos uso del namespace Windows.Phone.PersonalInformation.

Almacén de contactos

En este namespace Windows.Phone.PersonalInformation encontramos la clase ContactStore. Esta clase representa el almacén de contactos de nuestra aplicación. Si observamos como funciona el people hub en Windows Phone, veremos que todo contacto está asociado a un almacen: twitter, linkedin, facebook… Bien, con la clase ContactStore podremos acceder o crear el almacén de nuestra aplicación e indicar como se relaciona con el resto del sistema, usando el método CreateOrOpenAsync:

public async Task CreateContactsStoreForApplication()
{
    store = await ContactStore.CreateOrOpenAsync(ContactStoreSystemAccessMode.ReadWrite, 
                                                 ContactStoreApplicationAccessMode.ReadOnly);
}

Este método recibe dos parámetros: access y sharing. El primero, access, indica la forma en la que el sistema se relaciona con nuestro almacén. Podemos escoger en el enumerador ContactStoreSystemAccessMode entre dos valores: ReadOnly, el sistema solo puede leer los contactos y no se pueden editar o ReadWrite, el sistema puede modificar los contactos de esta ContactStore. El segundo parámetro, sharing, indica como se relacionan otras aplicaciones con nuestros contactos. Podemos escoger en el enumerador ContactStoreApplicationAccessMode entre dos valores: LimitedReadOnly, donde el resto de aplicaciones solo podrán obtener la imagen del contacto y su descripción, o ReadOnly, con la que otras aplicaciones podrán leer todas las propiedades de un contacto en nuestra Store.

En ningún caso indicamos el nombre del almacén, este es inferido directamente de nuestra aplicación.

Añadir contactos al almacén

Una vez que hemos creado o abierto nuestra ContactStore, podremos empezar a trabajar añadiendo contactos, consultando los ya creados o eliminando los que deseemos. Para crear o actualizar contactos usaremos la clase StoredContact que nos permitirá indicar los valores de las propiedades standard del contacto como pueden ser: nombre, email, etc…:

public async Task AddContactToStore(string name, string phone, string email, string alterEgo)
{
    StoredContact contact = new StoredContact(store);

    contact.RemoteId = Guid.NewGuid().ToString();
    contact.DisplayName = name;

    var contactProperties = await contact.GetPropertiesAsync();

    contactProperties[KnownContactProperties.Telephone] = phone;
    contactProperties[KnownContactProperties.Email] = email;

    ...
}

Siempre que creemos una nueva instancia de esta clase, tendremos que indicar una instancia de ContactStore válida, que usará internamente. Tras crear la instancia, existe una propiedad en especial muy importante: RemoteId. Esta identifica de forma única a nuestro contacto y no puede repetirse. En este caso le hemos indicado un GUID pero puedes usar cualquier cadena de texto siempre y cuando sea única. A continuación usamos el método GetPropertiesAsync, que nos devuelve un Dictionary<string, object> donde podremos indicar las propiedades del contacto, tales como teléfono, email, cumpleaños, compañía… para ello, usaremos el enumerado KnownContactProperties y así evitar el uso de “magic strings” que puedan producir errores. Pero, ¿Qué ocurre si nosotros tenemos campos que no coinciden con los del enumerado? ¿Estamos restringidos solo a usar estos y descartar el resto de información? No, para nada. Al igual que tenemos el método GetPropertiesAsync, disponemos de otro llamado GetExtendedPropertiesAsync este método nos permite obtener las propiedades “extras” que tiene un contacto, las no incluidas en la lista de propiedades conocidas. Se trata de un Dictionary<string, object> en el que podremos incluir cualquier propiedad que deseemos:

var extendedProperties = await contact.GetExtendedPropertiesAsync();

extendedProperties["AlterEgo"] = alterEgo;
extendedProperties["Home"] = "cave";

CONSEJO: Aunque de cara al ejemplo hemos usado “magic strings” para las claves, lo recomendable sería crear un enumerado, llamado por ejemplo ExtendedKnownProperties, que contenga todos los campos que necesitemos.

Una vez que hayamos terminado de añadir todos los campos extra que deseemos, es momento de guardar nuestro contacto. Para ello usaremos el método SaveAsync de la clase StoredContact, a continuación el código completo de nuestro método AddContactToStore:

public async Task AddContactToStore(string name, string phone, string email, string alterEgo)
{
    StoredContact contact = new StoredContact(store);

    contact.RemoteId = Guid.NewGuid().ToString();
    contact.DisplayName = name;

    var contactProperties = await contact.GetPropertiesAsync();

    contactProperties[KnownContactProperties.Telephone] = phone;
    contactProperties[KnownContactProperties.Email] = email;

    var extendedProperties = await contact.GetExtendedPropertiesAsync();

    extendedProperties["AlterEgo"] = alterEgo;
    extendedProperties["Home"] = "cave";


    await contact.SaveAsync();
}

En el ejemplo que he preparado, todo este código está aislado en un servicio llamado ContactsService que inyectamos en nuestra ViewModel, de forma que podamos invocarlo desde un comando:

public async void AddContactCommandExecute()
{
    this.IsBusy = true;

    await this.contactsService.CreateContactsStoreForApplication();
    await this.contactsService.AddContactToStore("Bruce Wayne", "555-123123", "bat@darkness.com", "Batman");

    this.IsBusy = false;
}

RECUERDA: Antes de poder ejecutar nuestra aplicación, tenemos que recordar añadir a las capacidades del manifiesto el uso de contactos (ID_CAP_CONTACTS) o fallará al intentar acceder a la ContactStore.

Si ejecutamos la aplicación ahora y presionamos el botón “Add a new contact to phone” se ejecutará el código que crea la store si no existe y añade el contacto. Después podemos ir al people hub y ver como aparece nuestra aplicación en la lista de proveedores de contactos y el contacto que hemos creado en la lista de contactos:

image

Consultar contactos del almacén

Bien, ya hemos visto como podemos crear nuestro propio almacén de contactos en el dispositivo y como añadir contactos al mismo. Ahora veamos como consultar esos contactos que hemos creado. Para ello usaremos el método CreateContactQuery de la clase ContactStore, a la que podemos pasarle una instancia de la clase ContactQueryOptions indicando la ordenación y los campos incluidos en la consulta:

ContactQueryOptions options = new ContactQueryOptions();

options.OrderBy = ContactQueryResultOrdering.FamilyNameGivenName;
options.DesiredFields.Add(KnownContactProperties.Email);
options.DesiredFields.Add(KnownContactProperties.GivenName);

ContactQueryResult query = store.CreateContactQuery(options);

En este caso, queremos que se ordenen los contactos por los apellidos y después por el nombre. También indicamos que deseamos incluir propiedades como el Email y el GivenName. a continuación ejecutamos el método CreateContactQuery con estas opciones y obtenemos nuestra consulta lista para ser ejecutada en la nueva instancia de ContactQueryResult. En este punto todavía no tenemos datos y no se ha realizado ninguna consulta, simplemente hemos preparado el “terreno”.

Ahora tenemos dos métodos en el objeto ContactQueryResult: GetContactsAsync, que nos devuelve una lista de contactos, y GetContactCountAsync, que nos indica el número de contactos en este almacén. Como podemos ver, no podemos realizar ningún tipo de filtrado en este punto. Llamamos al método GetContactsAsync y luego trabajamos con la lista de contactos devuelta, quedando nuestro método QueryContactByName de la siguiente forma:

public async Task<StoredContact> QueryContactByName(string name)
{
    ContactQueryOptions options = new ContactQueryOptions();

    options.OrderBy = ContactQueryResultOrdering.FamilyNameGivenName;
    options.DesiredFields.Add(KnownContactProperties.Email);
    options.DesiredFields.Add(KnownContactProperties.GivenName);

    ContactQueryResult query = store.CreateContactQuery(options);

    var contactList = await query.GetContactsAsync();

    return contactList.Where(c => c.DisplayName == name).First();
}

TRUCO: Si queremos que la interfaz de nuestro servicio (IContactsService) y nuestra ViewModel sean compatibles con Windows Phone 7 o Windows 8, es recomendable crearnos una clase que contenga los campos del contacto que necesitemos (dto o poco), en vez de devolver un StoredContact, así no tendremos problemas a la hora de implementar este servicio en otras plataformas.

Eliminar contactos del almacén

Ahora que sabemos como consultar un contacto y leerlo desde el almacén del dispositivo, veamos como eliminarlo. Esto es realmente sencillo, además nos apoyaremos en el método QueryContactByName que hemos creado anteriormente. Para eliminar un contacto usaremos el método DeleteContactAsync de la instancia de ContactStore que tenemos activa. A este método necesitamos indicarle el RemoteId del contacto, por lo que primero tendremos que buscar el contacto, si no lo tenemos ya:

public async Task DeleteContact(string name)
{
    var contactToDelete = await QueryContactByName(name);

    await store.DeleteContactAsync(contactToDelete.RemoteId);
}

¿Sencillo verdad? Si revisamos el código que hemos incluido en este artículo, veremos que no hay ninguna parte especialmente complicada. Clases con nombres muy descriptivos, métodos sencillos y directos. Creo que uno de los grandes éxitos de la gestión de contactos, y del API de Windows Phone 8 en general, es la sencillez de su uso.

Conclusión

Bueno, llegamos al fin. Esta es una de las características nuevas de Windows Phone 8 que habíamos pedido durante mucho tiempo los desarrolladores. Ahora es momento de demostrar las cosas increíbles que podemos hacer con los contactos! Como siempre, a continuación os dejo el código de ejemplo que hemos visto, perfectamente funcionando en un proyecto de Windows Phone 8, con MVVM, con Autofac, Comandos y todo bien comentado y en su sitio!!.

Un saludo y Happy Coding!


Published 5/1/2013 11:39 por Josué Yeray Julián Ferreiro
Comparte este post:

Comentarios

# Empieza el Megathon 2013, enlaces interesantes

Friday, April 12, 2013 1:21 PM por Josue Yeray

Hola a todos! Hoy 12 de Abril comienza el Megathon Windows 2013, en esta edición se pueden presentar

# Tips & Tricks de desarrollo para Windows Phone

Wednesday, March 19, 2014 12:28 PM por MSDN España

Te presentamos nuestra colección de Tips & Tricks de desarrollo de apps para Windows Phone