A Metro style interface to Lync – Part 4

This article was imported from codelync.com

Creating the WCF Service

As I discovered in my previous post, we can’t directly use the Lync API from a Metro style app, so to create a Metro style UI for Lync we’re going to build a WCF wrapper around the Lync API.

The requirements for the app are to get a list of the users Lync contacts, and to keep their presence updated correctly. To achieve this, the WCF Service needs a simple interface:

[ServiceContract(
    Namespace="http://www.codelync.com/prototype/lyncmetro",
    SessionMode = SessionMode.Required,
    CallbackContract = typeof(ILyncMetroCallback))]
public interface ILyncMetro
{
    [OperationContract]
    List<LyncGroup> GetContacts();
}

public interface ILyncMetroCallback
{
    [OperationContract(IsOneWay = true)]
    void AvailabilityChanged(string sipUri, AvailabilityEnum availability);
}

Supported by the following data contract:

[DataContract]
public class LyncContact
{
    [DataMember]
    public string SipUri { get; set; }
 
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public Byte[] Image { get; set; }

    [DataMember]
    public AvailabilityEnum Availability { get; set; }
}

[DataContract]
public class LyncGroup
{
    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public List<LyncContact> Contacts { get; set; }
}

[DataContract(Name = "Availability")]
public enum AvailabilityEnum
{
    [EnumMember]
    Offline,
    [EnumMember]
    Away,
    [EnumMember]
    Busy,
    [EnumMember]
    Online
}

With the following implementation:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class LyncMetro : ILyncMetro
{
    ILyncMetroCallback _callback = null;

    LyncMetro()
    {
        _callback = OperationContext.Current
            .GetCallbackChannel<ILyncMetroCallback>();
        Lync.AvailabilityChanged += Lync_AvailabilityChanged;
    }

    public List<LyncGroup> GetContacts()
    {
        return Lync.GetContacts();
    }

    void Lync_AvailabilityChanged(
        object sender, AvailabilityChangedEventArgs e)
    {
        _callback.AvailabilityChanged(e.SipUri, e.Availability);
    }
}

Note that I’m wiring up an event handler to the AvailabilityChanged event on a static class called Lync. The event handler calls the AvailabilityChanged method in the ILyncMetroCallback WCF interface – this will ultimately call a method in the client application that handles a contact’s availability changes.

The Lync static class encapsulates interaction with the Lync client. As well as logic to sign in and out of Lync in UI Suppression mode (not shown, for brevity), it contains a public method for retrieving contacts:

internal static List<LyncGroup> GetContacts()
{
    List<LyncGroup> lyncGroups = new List<LyncGroup>();

    foreach (var group in _client.ContactManager.Groups)
    {
        var lyncGroup = new LyncGroup()
        {
            Name = group.Name,
            Contacts = new List<LyncContact>()
        };

        lyncGroups.Add(lyncGroup);

        foreach (var contact in group)
        {
            lyncGroup.Contacts.Add(GetLyncContact(contact));
            contact.ContactInformationChanged += contact_ContactInformationChanged;
        }
    }

    return lyncGroups;
}

The _client variable is a member variable of type Microsoft.Lync.Model.LyncClient, found in the Lync Client API. It has a ContactManager property, which encapsulates functionality for managing contacts. The Groups property used here returns all contact groups that the user has defined in the Lync client.

Note that I’m wiring up an event handler to capture any availability changes. The event handler looks like this:

 static void contact_ContactInformationChanged(object sender, ContactInformationChangedEventArgs e)
{
    if (e.ChangedContactInformation.Contains(ContactInformationType.Availability))
    {
        Contact contact = (Contact)sender;
        var availability = GetAvailability((ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability));

        OnAvailabilityChanged(contact.Uri, availability);
    }
}

The OnAvailabilityChanged method isn’t shown here. This method just raises the AvailabilityChanged event that is defined on the Lync class.

The class also contains 3 helper methods. GetAvailability converts from a Lync ContactAvailability enumeration to the AvailabilityEnum enumeration defined in our WCF data contract:

 static AvailabilityEnum GetAvailability(ContactAvailability contactAvailability)
{
    AvailabilityEnum availability = AvailabilityEnum.Offline;

    switch (contactAvailability)
    {
        case ContactAvailability.Away:
        case ContactAvailability.BusyIdle:
        case ContactAvailability.FreeIdle:
        case ContactAvailability.TemporarilyAway:
            availability = AvailabilityEnum.Away;
            break;

        case ContactAvailability.Busy:
        case ContactAvailability.DoNotDisturb:
            availability = AvailabilityEnum.Busy;
            break;

        case ContactAvailability.Free:
            availability = AvailabilityEnum.Online;
            break;

        default:    // Invalid, None, Offline
            availability = AvailabilityEnum.Offline;
            break;
    }

    return availability;
}

GetLyncContact takes a Lync API Contact object and returns it as a LyncContact object:

static LyncContact GetLyncContact(Contact contact)
{
    var lyncContact = new LyncContact();

    lyncContact.Name = contact.GetContactInformation(ContactInformationType.DisplayName) as string;
    lyncContact.SipUri = contact.Uri;
    lyncContact.Availability = GetAvailability((ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability));
    
    try
    {
        lyncContact.Image = GetImageBytesFromStream(contact.GetContactInformation(ContactInformationType.Photo) as Stream);
    }
    catch(Exception)
    {
        lyncContact.Image = File.ReadAllBytes(Path.Combine(Application.StartupPath, "NoPhoto.png"));
    }

    return lyncContact;
}

GetImageBytesFromStream converts a stream representing a contacts image into a byte array:

static Byte[] GetImageBytesFromStream(Stream stream)
{
    byte[] bytes;

    using (BinaryReader br = new BinaryReader(stream))
        bytes = br.ReadBytes((int)stream.Length);

    return bytes;
}

Next: Creating the Metro style app

This is part 4 in a series of 5:

Paul Nearney Written by:

Paul is a software solution architect specialising in Microsoft Teams, and the founder of Chimu Software