Windows Phone 7 is still not released, and the APIs, SDK, emulator and so on is till far from complete. Having said that, people are still getting revved up about the platform and have already started to develop on it. I think this is cool, and makes me believe that there will be some really good apps on the market place already on the launch day. Unfortunately, I have been too busy to get too down a dirty with it. I definitely hope to change this, and have decided to build a game. Hopefully it will be complete by launch and I can sell 2 or 3 copies.
But, to be honest, I still feel that the SDK and APIs are changing a bit too much for me to get REALLY excited about it. I really hope that there is a new refresh on the way soon. Cause at the present, I find that there are just too many unknown things that make the development hard. You never know if the issue you are experiencing is due to you doing something wrong or due to a bug in the OS. And if it is a bug in the OS, you don’t really know when or how it will be fixed. And I really don’t want to spend my time working around things now, just to end up with weird an unnecessary workarounds in the code when the final version is released. Having said that, it still doesn’t stop you from playing around with the platform. I can still build most of my game logic and even test it in the browser based version of Silverlight while I wait for the next refresh. One feature that I wanted to try out straight away however is the push notification. Why? Well…let’s just say that I have my reasons…
So why write a post about push notification? Well…first of all, I think it is a feature that is going to be used a lot. Since the phone doesn’t support multitasking and applications running in the background, pushing out notifications is the only way to notify the user. Next, I couldn’t actually find a single great source with information about how to implement it. There are a few, I know, but they all seem to miss a tiny piece of information here and a tiny piece of information there. This caused me a lot of grief when I tried it myself, so I thought I would try to give a “definite guide to push notifications”. At least a guide on how to get it to work with the current beta.
So, how does push notifications work? Well, it really isn’t that complicated, but there are a couple of players involved. First of all, there is the phone. An application that wants notifications, creates a new notification channel. This channel is registered on the phone and then the phone handles the rest. When a channel is created, the phone will communicate with the second player, which is Microsoft. Microsoft’s push notification service, which is a cloud hosted service, will register the channel and return a unique Uri that identifies the channel. This Uri must then be registered with the last player, your server. So when the channel has been created, the application must call home and tell the back-end that is wants to get some form of notification and supply the Uri to use to send the notification. The server can then send standard HTTP POST requests to that Uri to get notifications sent to the phone.
That is actually everything you need to do. A simple three step procedure
1. Create a channel and get hold of its unique Uri
2. Tell your server what that unique Uri is
3. Send HTTP POST requests to that Uri to get the notifications sent to the phone
Ok…that sounds pretty simple. Can I send whatever I want in a push notification? Will my application get started and handle the notification? No…and no… There are 3 different kind of notifications available, tile, toast and raw.
A toast notification will show a “toast” on the screen when received. Each toast can contain 2 strings. They will be shown after each other, the first one bold and the second regular weight. The toast is “connected” to your application, so if the user clicks it, it will start the application. It will however not tell the application in any way that it has started due to a toast. And no, you can’t modify the way the toast looks. It will look like Microsoft has defined it.
The next type, the tile notification, is sent out to cause a home screen tile to change. It can include a title, an image and a number. It will cause the tile on the homepage (if the application is pinned to the homepage) to update based on the sent information. The image will be set as the background of the tile. The title will be written on top of it in the bottom left corner. And the number, if larger than 0, to be shown in an ellipse in the top right corner. This can be used to indicate the amount of missed updates or messages or whatever. It can however only go to 99…
The final type, the http notification, is just that. A raw http notification. It can only be intercepted by an application is running and can contain anything you want…as long as it is less that 1024 bytes. But…I do want to push the fact (no pun intended) that it will only be intercepted if the application is running.
So why do I need to write a blog post about it? Well…there are a few issues when setting up this using the current beta.
Let’s start at the phone end. First I need a unique way to identify the application. Since I am going to contact my server and tell it what my channel Uri is, I need to have some form of id. Well…I don’t have to, but since the Uri might change, this is not a unique identifier and I really don’t won’t to end up with a gazillion old Uri’s in my database. That will just cause problems when I decide to push out a notification to all my clients. This is obviously easily solved if you have an application where you login or have a unique username or something. In my case I don’t have a login, so I just generate a unique id for the app with the following code
Guid _appId;
if (IsolatedStorageSettings.ApplicationSettings.Contains("AppID"))
{
_appId = (Guid)IsolatedStorageSettings.ApplicationSettings["AppID"];
}
else
{
_appId = Guid.NewGuid();
IsolatedStorageSettings.ApplicationSettings["AppID"] = _appId;
}
And yeah…I use the sweet little IsolatedStorageSettings class. It makes it so easy to store little settings in isolated storage without having to create an Xml file or something…
Next, I need to open the notification channel. The class used to do this is called, not completely surprisingly, HttpNotificationChannel. As there might be one registered from an earlier execution of my application, I start by checking for an existing one. As each channel needs name, I can easily see if one exists by using the following code
try
{
_channel = HttpNotificationChannel.Find(CHANNEL_NAME);
}
catch (NotificationChannelNotFoundException) { }
As you can see, I wrap it in a try/catch and make sure I catch any NotificationChannelNotFoundException. An exception like that means that the channel wasn’t found (doh!). I really hope that the HttpNotificationChannel.Find() method return null in the future instead of throwing and exception…
If I don’t find a channel, I need to create one. This is really easy to do. Just create a new HttpNotificationChannel, passing in the name to use for identification. Then hook up a handler to the ExceptionOccurred event as well the ChannelUriUpdated. The exception one is pretty obvious, the second one might not be. As I said earlier, when opening a channel, a unique Uri is returned. But seeing that Silverlight does all external communication asynchronously, we need to listen for an event. So, ChannelUriUpdated just means that a Uri has been returned and that everything has gone well.
As soon as the handlers are hooked up, I execute Open().
_channel = new HttpNotificationChannel(CHANNEL_NAME);
_channel.ChannelUriUpdated += OnChannelUriUpdated;
_channel.ExceptionOccurred += OnExceptionOccurred;
try
{
_channel.Open();
}
catch (NotificationChannelOpenException)
{
throw;
}
As you can see, I am doing a really weird try/catch thing. I am not really catching anything. I am just re-throwing. I do this to make a point. The emulator has an issue that causes it to throw NotificationChannelOpenException. During normal use, I assume the exception should come through the event, but for now it comes like this. The reason for it is that it takes the emulator up to 2 minutes to initiate all the network stuff. So if you get an exception thrown right away when you try to open, try letting the emulator be on for a while and then try again. (Issues like these are the real reason for this blog post)
When the ChannelUriUpdated event is raised, there are a few things I need to do. First of all, I detach my handlers for the ChannelUriUpdated and ExceptionOccurred events. Next, I need to tell the channel that I am interested in getting toasts and tile updates. This is not automatically done. The http notifications work straight up without having to tell the channel…at least I think so…I didn’t actually try using it without hooking up the channel to handle toast and tile notifications.
The way to tell the channel that I want tile and toast notifications is to bind it by calling BindToShellEntryPoint() and BindToShellNotification(). ShellEntryPoint is the awesome code name for a tile and ShellNotification is a toast.
_channel.BindToShellEntryPoint();
_channel.BindToShellNotification();
When binding to the ShellEntryPoint, you can pass in an instance of ShellEntryPoint to use as the default tile.
Next, I need to tell my server what my channel Uri is. In this case it does so using a webservice. But as this has nothing to do with the actual push notification, I will skip that code though and jump straight to the next step, which is to hook up handlers for the receive events. There are obviously 3 of them, ShellNotificationReceived, HttpNotificationReceived, ShellEntryPointNotificationReceived, as well as the ExceptionOccurred that is a good idea to keep an eye on as well.
When the application is running, the toasts will not be displayed and the information will instead be routed to the running application. The tile notifications will also be available to the application, but they will also cause the desired changes to the home screen. And finally, the http notifications will, as mentioned before, only be routed to the running applications.
Remember that you need to hook up these handlers every time the application starts, as well as when resuming from a suspended state. I don’t know if it is mandatory to hook them up if coming back from a suspended state, but on the other hand the should be disconnected when the application is suspended… So in this case, if I do find a channel when looking for one at the startup, I run off and hook up my handlers instead of trying to open the connection.
So what do you get passed into the handlers? Well, the EventArgs used is called NotificationEventArgs and HttpNotificationEventArgs. The NotificationEventArgs will be used for the ShellNotificationReceived and ShellEntryPointNotificationReceived events. It includes a property called Collection. This collection contains all of the data passed to the phone in the notification.
In ShellNotificationReceived it contains to strings. The 2 texts. The key for them in the collection is wp:Text1 and wp:Text2. For some reason you need to include an XML namespace (you will sort of see why later).
In the ShellEntryPointNotificationReceived you get 3 strings. The title, the count and the image Uri sent in the tile notification. They are available with the following names: Title, Count, BackgroundImageURI. This is kind of funky as they do not include any XML namespace… I guess the keys for the ShellNotificationReceived arguments will change in a future refresh. At least I hope so…
The HttpNotificationReceived is a bit different. Its HttpNotificationEventArgs contains a property called Notification that has a property called Body. The Body property is of type Stream and contains the raw data sent in the notification. So you will have to read the Stream and do something with it manually…
That is really everything you should need to do. But it isn’t. Here is another little interesting hiccup. In the projects Properties directory, you will find a WMAppManifest.xml file. This contains a lot of the settings for the phone application. In here you can for example set up a default tile, which is called PrimaryToken (funky since it is called a ShellEntryPoint in the code). The reason that I bring up this file is that the channel will fail to open if you don’t make a change in here. In the App element, there is an attribute called Publisher. This is empty by default, but needs to have a value. At least in the current beta…
After we have changed that xml file, the phone end is actually done. And since I can’t really show you how Microsoft’s push notification service is implemented, I guess I will just have to go on and show how I send messages to my phone.
In my case, that piece of functionality is split into 2. A sender part, which is a very simple WPF application. It has very little functionality. It presents a simple UI consisting of a tab control. It contains one tab per notification type. Each tab has input controls corresponding to the data we can pass in the notification. It isn’t very useful as such, but it is good to use for testing purposes…so I guess it IS useful… The sender application relies on the same webservice that the phone used to register its Uri with. I will get back to that webservice in a short while.
As I said before, the UI is very basic. The first tab, the one for the toast notifications, contains 2 TextBox controls and a Button.
<TabItem Header="Toast">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="Text 1:" VerticalAlignment="Center" />
<TextBlock Text="Text 2:" VerticalAlignment="Center" Grid.Row="1" />
<TextBox x:Name="ToastText1" Grid.Column="1" Width="300" Margin="20,5" />
<TextBox x:Name="ToastText2" Grid.Column="1" Grid.Row="1" Width="300" Margin="20,5" />
<Button Content="Send Toast" Grid.Column="1" Grid.Row="2" Margin="20,5" Click="SendToast_Click" />
</Grid>
</TabItem>
As you can see from the above XAML< I haven’t even bothered to make a VM for it or anything. It is a hacked together debug tool…
The second tab, the one for the tile notifications, contains a TextBox for the title, a TextBox for the count and a ComboBox for the image to use. The reason that I use a ComboBox for the image is that the current version of the emulator is a bit limited. It only supports tile notifications referencing images on the “device”. So I need to use an image that I have included in my application. This will be fixed in the future. But for now, you will just have to add an image or two to your phone application project and set its build action to “Content”. In my case, I added three images in an folder called “Images” in my phone app project.
<TabItem Header="Tile">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="Title:" VerticalAlignment="Center" />
<TextBlock Text="Count:" VerticalAlignment="Center" Grid.Row="1" />
<TextBlock Text="Image:" VerticalAlignment="Center" Grid.Row="2" />
<TextBox x:Name="TileTitle" Grid.Column="1" Width="300" Margin="20,5" />
<TextBox x:Name="TileCount" Grid.Column="1" Grid.Row="1" Width="25" Margin="20,5" HorizontalAlignment="Left" />
<ComboBox x:Name="TileImage" Grid.Column="1" Grid.Row="2" Width="300" Margin="20,5" SelectedIndex="0">
<Sys:String>Images/Icon1.png</Sys:String>
<Sys:String>Images/Icon2.png</Sys:String>
<Sys:String>Images/Icon3.png</Sys:String>
</ComboBox>
<Button Content="Send Tile" Grid.Column="1" Grid.Row="3" Margin="20,5" Click="SendTile_Click" />
</Grid>
</TabItem>
And the final tab is for the raw notifications. This is probably the simplest one, it has a TextBox and a Button…
<TabItem Header="Raw">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="Message:" VerticalAlignment="Top" Margin="0,5,0,0" />
<TextBox x:Name="Rawtext" Grid.Column="1" Width="300" Height="100" Margin="20,5" />
<Button Content="Send Message" Grid.Column="1" Grid.Row="2" Margin="20,5" Click="SendRaw_Click" />
</Grid>
</TabItem>
The code behind this page is just ridiculous, so I will leave it out. Every button click basically just new up a service client and call the corresponding notification method. So instead, I will go on and look at that service…
So the last piece of the puzzle is the web application that hosts the awesome webservice. It is a simple WCF webservice with nothing special. The interface for it looks like this
[ServiceContract]
public interface ISubscriptionService
{
[OperationContract]
void Subscribe(Guid id, string uri);
[OperationContract]
void SendToast(string text1, string text2);
[OperationContract]
void SendTileUpdate(string title, int count, string imageUrl);
[OperationContract]
void SendRawMessage(string message);
}
So the first method I used in the phone application was the Subscribe method. It literally stores the app Guid and the Uri in a Dictionary<Guid,Uri>. I didn’t need anything more complicated in this scenario…so I will leave that…
The 3 other methods basically work in exactly the same way. They create a message to send, and then sends that message using an HTTP POST for each of the Uris in the Dictionary. The SendRawMessage() is the simplest. It looks like this
public void SendRawMessage(string message)
{
byte[] msg = System.Text.Encoding.UTF8.GetBytes(message);
foreach (var uri in _clients.Values)
{
PostMessage(uri, msg, 3);
}
}
It takes the passed in message and converts the strings to an byte array and passes is to a magic method called PostMessage(). Very simple. Especially since I will ignore PostMessage() for now.
I do however want to mention that last 3 in the method call. Each of the notifications can be sent using a so called notification class. Basically a priority saying how soon it needs to be delivered. It is passed a header in the HTTP call. It is a number defined by Microsoft. You can find more information about it here: http://msdn.microsoft.com/en-us/library/ff402545(VS.92).aspx.
This number is another reason for me blogging this. I didn’t read that page I linked to well enough, which caused me 2 hours of debugging. The lower the number, the faster the message should be delivered. So I assumed 1 one deliver it immediately all the time… It doesn’t… The lowest number is 3 for raw notifications, 1 for tile and 2 for toast…obviously! :)
The SendToast() and SendTileUpdate() are almost as simple. The only difference is that the message needs to follow a specific format. It basically needs to be an XML fragment that is sent. But since the XML fragments are identical all the time except for a few parameters, I have actually skipped the whole XML stuff and just stored the XML as a string. A string with {n} in them so that I can easily insert my parameters by calling string.Format(). So the code for the methods look like this
public void SendToast(string text1, string text2)
{
string message = string.Format(toastMessage, text1, text2);
byte[] msg = System.Text.Encoding.UTF8.GetBytes(message);
foreach (var uri in _clients.Values)
{
PostMessage(uri, msg, 2);
}
}
public void SendTileUpdate(string title, int count, string imageUrl)
{
string message = string.Format(tileMessage, title, count, imageUrl);
byte[] msg = System.Text.Encoding.UTF8.GetBytes(message);
foreach (var uri in _clients.Values)
{
PostMessage(uri, msg, 1);
}
}
And the XML strings are quite simple as well
static string toastMessage = "Content-Type: text/xml\r\nX-WindowsPhone-Target: toast\r\n\r\n" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>{0}</wp:Text1>" +
"<wp:Text2>{1}</wp:Text2>" +
"</wp:Toast>" +
"</wp:Notification>";
static string tileMessage = "Content-Type: text/xml\r\nX-WindowsPhone-Target: token\r\n\r\n" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Token>" +
"<wp:Title>{0}</wp:Title>" +
"<wp:Count>{1}</wp:Count>" +
"<wp:Img>{2}</wp:Img>" + // Issues in current release...?
"</wp:Token>" +
"</wp:Notification>";
As you can see, it adds a couple of headers inline as well. I think this will be changed to actually use headers in the call in the future, but for now I think they need to be in the message like this.
The final little method, the one I left out is just about creating an HTTP POST. It also sets the ContentType header to text/xml and adds the X-NotificationClass header. This is nothing complicated if you have sent POST messages before, but here it is…
private void PostMessage(Uri uri, byte[] msg, int notificationClass)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
req.Headers.Add("X-NotificationClass", notificationClass.ToString());
req.Method = "POST";
req.ContentType = "text/xml";
req.ContentLength = msg.Length;
using (Stream requestStream = req.GetRequestStream())
{
requestStream.Write(msg, 0, msg.Length);
}
try
{
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
//response.Headers["X-NotificationStatus"];
//response.Headers["X-SubscriptionStatus"];
//response.Headers["X-DeviceConnectionStatus"];
response.Close();
}
catch (Exception ex)
{
}
}
That’s all you need to do to send messages…
But there is a little thing I want to mention. In the future, you will be able to send secure messages. Basically you register a certificate with Microsoft to make sure that messages are only sent from you to your application. Otherwise it would be quite “easy” to create spam. I don’t know if it will be mandatory, or an extra feature…
The source code for my project is downloadable here as usual: Demo.PushNotification.zip (95.00 kb)
I have set it up so that F5 will start all three projects at once, but it doesn’t work that great all the time. For some reason I get an “object is null” pop-up when I do it. It also means that the WP7 app isn’t deployed correctly to the emulator. If that happens, right-click the projects one at the time and choose to start debugging. Do it in the right order though, web app, phone app and then wpf app…
If you have any questions, don’t hesitate to e-mail me or post a comment… Cheers!