Determining/Controlling your Environment - Presence (2)
Okay, so in previous post we tried to understand basic needs for being connected with our People and Groups. Let's review the main goals (we'll update them in future when needed).
People & Groups
1. Goals:
Have one place in Windows to access your friends, teammates, and other people you know, and manage connections with them
2. Things I expect from this app
- One single easy People & Groups browser to easily see all of your people, find those you need to connect now
- One single easy way to connect with these people using different ways - Email/IM/Telephone/VoIP
- Publish presence to any IM/Telephone/VoIP network I use if possible, and try to make it context-sensitive
I think this should be enough to start. Oh, I forgot about Developers, right:
3. Developer Perspective
- API to access current user's People & Groups, retrieve presence for his contacts
- Easy way for manageability of presence in 3-rd party Presence published app - it can be IM Client, or say Facebook, and vise versa.
Developing People & Groups Framework
Framework - nice word, right? So we need a way to get your connections from existing sources, be able to publish presence using your IM Clients, update your presence using them (at least) and, finally, see all your people in one place.
So how the solution should be developed?
First, can we get contacts from these sources? yeah.
IM Clients
- Windows Live Messenger has its Messenger API Type Library (a part of msnmsngr.exe) - COM
- Skype has its Skype4Com.tlb that exposes its functionality in a COM way
- Office Communicator also has COM interface (and as far as Office Communicator is in fact just an old good Windows Messenger pooled from Windows XP with no way to SignIn to .NET Messenger Service and is optimized for Active Directory/Outlook/Live Communications Server, it has a very same COM interface like the one Windows Live Messenger has
Social Networks
- Facebook - there is a Facebook dll for .NET, so you can manage your user's account through it - I mean publish your presence etc. I think we can also see your friends from it but let it alone for now
- Other networks - well Windows Live Spaces as far as I know do not provide any web services to work with them but I'm not sure.
Okay I think it is enough to start :)
How We Can Design The Solution?
We can start with writing one library that will have access to all IM clients and just pull and update data there, right? Actually this would be wrong design. We can't be sure that users do have all apps we would support in this lib, also what about versioning?
So we need a model where we can attach IM/Presence/People provider to system, pull and update data from/into it, and use it, right?
In COM, there is DCOM and marshalling and all that stuff, for .NET - Remoting & Reflection. Well until now.
Because now we have... right - Managed Add-Ins Framework!
It lives in System.AddIn and System.AddIn.Contracts libs in .NET Framework 3.5 which you can download here from our Microsoft Download Center:
To read more on this stuff, read these blogs & articles:
- Jack Gudenkauf (JackG) WebLog - PDC05 - Managed AddIn Framework (MAF)
- Customization, Automation, Addins and Life - What is MAF-
- CLR Inside Out- .NET Application Extensibility, Part 2 -- MSDN
- Greg's Cool [Insert Clever Name] of the Day- Managed Addin
you can even download some samples here:
nice start, right? It looks like we can now create what we need? Each Add-In will be in its own AppDomain, it's own thread, it can be securely pulled off from memory, we can set security settings (Security Zone) for it, right?
Actually yes, but I'm afraid that all these links won't help you to write your own first implementation of MAF for your solution.
So I seek again and found the following great introduction to MAF written by Guy Berstein:
How To- Build an Add-In using System.AddIn
This rocks, I might have to say, and now we can build our own Presence/People/IM framework using MAF!
1. Developing Framework: API we need to call from AddIn
First, we need to create an abstract HostView class that will enable consumers to access functionality they look for in addins:
These are methods we want to be able to invoke from each AddIn.
GetIMClientId - we want to provide with unique Id each client - here I mean IM client - say WLM, or Skype, or Office Communicator, etc.
GetPresenceInfo - this helps me to get PresenceInfo only for one contact not for all.
IsSignedIn - this helps me to be sure that user already signed in using this IM client so that Framework could be sure that all other methods would be invoked to the actual client with actual data not to not-signed in IM client :)
TryGetAllContacts - I believe this should be asked once a day (or once at each Windows startup) so that we could get all contacts user have in this IM client.
TryGetContactById - We want each AddIn to store a small hashtable where each contact will have it's own Id and Id for that particular network. The idea is that we want to be sure that each contact in main Contacts Store will be in current IM client and vise versa. So if anything will be updated on one of these sides, another side would be able to update only records that it is needed to update, not to update all records again - this will save a lot of CPU time :)
tryGetContactByIMAddress - as far as real unique Id for IM Network (that we can retrieve) is IM Address, we want to also be able to retrieve Contact By IM Address, too.
TrySetMyPresenceInfo - this will enable me to publish my presence info using this particular client we manage through this addin.
2. How To Represent Data We Need To Get From Addin
Here we go. We need a container that will allow us to send all data we need from different clients to the Framework and its customers.
This container can be an XML file or a Serializable class - no matter, because the class will actually be converted into an XML file, and to play with XML file we can use xsd.exe tool from SDK to get auto-generated class, so let's try to start with a class:
This is an interop class for all data exchange operations within Framework and Addins. Contact has to have name (DisplayName), collection of addresses (IM/Emails/Phones), have current PresenceInfo, unique ContactId (which is unique for ContactStore in Framework), and, finally, have current presence info.
Here is the PresenceInfo class I've designed - I added enum PresenceStatus and Descriptive message for those situations when basic PresenceStatus can't help:
Finally, PresenceStatus is:
Notable are OutOfOffice, Invisible, DoNotDisturb statuses - they are working different in different clients. OOF is working primarily with Office Communicator, while DoNotDisturb is a really DoNotDisturb in both Office Communicator and Skype, while Windows Live Messenger doesn't support this presence status.
DoNotDisturb - this is when I'm so busy that I do not want to be interrupted by someone's chat messages
OutOfOffice - is for situations when I do not have any access to Internet/corporate network at all, or I am just at home, not in office, so I can publish this status to tell to my colleagues that I can't meet with them face to face today.
Invisible - helps in situations when DoNotDisturb is impossible to use - particular one of my colleagues in our Russian DPE team uses it in Windows Live Messenger when he doesn't want to be interrupted - and Busy status in WLM doesn't help here :(
As of other presence statuses, notable is also OnTheMeeting - this one should be set when you are really on the meeting. In future, Semantics should be capable to pull data from Outlook / Windows (Live) Calendar and set this status each time you'll be on the real meeting :)
As of MailAddress, IMAddress and TelephoneNumber - they all store strings, in fact :)
IMPORTANT
Each module in the MAF pattern which has methods that use these classes has to reference to library that has definitions of these classes.
So I added one Side Agnostic project, library, where I keep these classes and made references in all solution projects.
The next step will be to develop AddInView class which is not really different from HostView:
The thing we should note is that we do not reference HostView at all. This is because of concept of MAF we implement:
So the real place through which Host and AddIns communicate is at Isolation Boundary level - IContract :)
Let's take a look onto the Contract:
Of course it should be looking in a same way our Host and AddIn Views are.
Now, let's try to implement the Windows Live Messenger AddIn for this experimental Framework we've just built:
Of course here is a place where our implementation of AddIn starts. We will use Messenger API Type Library to access Windows Live Messenger functionality:
You see, we referenced MessengerAPI (it was auto-generated from Messenger API Type Library), and we referenced the ContactContext assembly - this one stores definitions for Contact, IMAddress, MailAddress, TelephoneNumber, PresenceStatus and PresenceInfo items.
I'm not going to make you bored with actual implementation, just wanted to say that we need to check if the IM Client (in this case, WLM) is already Signed In or not. To accomplish this, we implement IsSignedIn method to do that job. Fine.
Now we can create a test project... No, actually, we can just retrieve Side Agnostic representation of items from IM Client - Contact, addresses, all that stuff. But where should we store it then?
Actually I wanted to create a database in SQL Server for that job, and maybe in future will do that, but now it's worth to remember that Joe Castro from Windows Live Messenger Team created a cool Contacts.Net library that enables you to store Contacts in Windows Contacts folder in Windows Vista. Have to add that I do not want to use this store in future as far as it is not relational but for now it is okay - we wanna test our Framework, right? :)
So we need to build a library that will call Contacts.Net ContactManager and do all stuff like searching for existing contacts, updating them, or creating new contacts.
I've created one class for it, ContactContext, to make all these operations working as expected:
So now we can write a test project and try out new Framework, right? :)
Here is our Test app:
As far as we use Windows Contacts in Windows Vista to store contacts, here is how my Contacts folder looks like before launching this App:
3 contacts, right? :)
Nice, right? :))) haha, let's try it:
Cool, it works! :))) We now see people from Windows Live Messenger in my Windows Contacts folder, too:
What is important, I do not want to have new Contact Items for existing people (like those who are already in my Contacts folder), so the test app checked existing contacts if there are no same contacts in folder:
List<Contact> curContacts = new List<Contact>();
this.listBox1.Items.Clear();
Microsoft.Communications.Contacts.Contact winCont = new Microsoft.Communications.Contacts.Contact();
if (addinWLMInstance.TryGetAllContacts(out curContacts))
{
foreach (Contact semC in curContacts)
{
if (cc.IsContextContactStored(semC, out winCont))
{
winCont = cc.UpdateStoredContactWithContextNewData(semC, winCont);
SimpleContactControl scc = new SimpleContactControl();
scc.DisplayName = winCont.Names.Default.FormattedName;
scc.IMServiceName = winCont.IMAddresses.Default.Protocol;
if (semC.PresenceInfo.PresenceStatus != PresenceStatus.Offline)
{
scc.IsOnline = true;
}
else scc.IsOnline = false;
this.listBox1.Items.Add(scc);
}
else
{
winCont = cc.CreateNewStoredContactFromContextContact(semC);
SimpleContactControl scc = new SimpleContactControl();
scc.DisplayName = winCont.Names.Default.FormattedName;
scc.IMServiceName = winCont.IMAddresses.Default.Protocol;
if (semC.PresenceInfo.PresenceStatus != PresenceStatus.Offline)
{
scc.IsOnline = true;
}
else scc.IsOnline = false;
this.listBox1.Items.Add(scc);
}
}
}
So each time we will check for contacts using AddIn, we should check if contact is already in store, update it or if it is not in store, create new Stored contact for it.
Disclaimer
Of course, this is just a test, and calling TryGetAllContacts is very expensive for CPU, so next time we will actually use Hashes to check if contacts changed or not, to be able to minimize our calls to addins :)
Conclusion
So far, we created a prototype for People & Groups Framework, using MAF - System.AddIn functionality of .NET Framework 3.5
Check out next posts to understand how to minimize calls and usage of CPU time using such a solution.
Technorati Tags: Managed Addins Framework,IM,Windows Live Messenger,Presence Providers,Research,Semantics,System.AddIn,How-To