WP7 Push Notifications Part 2

So far we’ve looked at the importance of notifications on the Windows Phone platform along with the different types of notifications that are available to your application. In this post we’re going to go through setting up your application to receive notifications and how to actually send notifications.

The first step in configuring your application to receive notifications is to register with the Microsoft hosted Push Notification Service. This is done by creating an instance of the HttpNotificationChannel class and calling the Open method. The Open method is an asynchronous method which will establish the communication channel between the Push Notification Service and your application running on that particular device. Once the channel has been opened the Channel Uri (which you can think of as a unique identifier for the channel) is returned to the application via the HttpNotificationChannel instance. A ChannelUriUpdated event is raised on the HttpNotificationChannel instance, allowing your application to detect when the channel has been setup.

The following code illustrates creating the HttpNotificationChannel, wiring up the ChannelUriUpdated event and then opening the channel. The Open method will return immediately, with the ChannelUriUpdated event being raised shortly afterwards once the channel has been setup. Note that the ChannelName is an arbitrary name that you assign to the channel for your application.

private const string ChannelName = "MyApplicationChannel";

public HttpNotificationChannel Channel { get; set; }
public bool ChannelIsActive { get; set; }

private void EstablishNotificationChannel() {
    Channel = new HttpNotificationChannel(ChannelName);
    Channel.ChannelUriUpdated += HttpChannelChannelUriUpdated;
    Channel.Open();
}

private void HttpChannelChannelUriUpdated(object sender, NotificationChannelUriEventArgs e) {
    UpdateChannelIsActive();
}

private void UpdateChannelIsActive() {
    ChannelIsActive = Channel != null && Channel.ChannelUri != null &&
                                      !string.IsNullOrEmpty(Channel.ChannelUri.OriginalString);
}

Once open, the channel may continue to exist even when your application is closed or tombstoned. This is to ensure that tile and toast notifications are received by the device when your application is not active. If your application attempts to create a new channel every time it runs an exception will be raised as you can only have one active channel at a time. To resolve this issue, before you attempt to create a new HttpNotificationChannel you need to call the static Find method to see if there is already a channel available, as illustrated in the following code.

private void EstablishNotificationChannel() {
    Channel = HttpNotificationChannel.Find(ChannelName);
    if (Channel == null) {
        Channel = new HttpNotificationChannel(ChannelName);
        Channel.ChannelUriUpdated += HttpChannelChannelUriUpdated;
        Channel.Open();
    }
    else {
        Channel.ChannelUriUpdated += HttpChannelChannelUriUpdated;
    }

    UpdateChannelIsActive();
}

You should note that even if the HttpNotificationChannel exists, you should still wire up the ChannelUriUpdated event because there are scenarios where an HttpNotificationChannel instance will be returned but the channel hasn’t had an opportunity to initialise. In these cases, once the channel is setup the ChannelUriUpdated event will be raised.

So far all we’ve done is establish the channel through which notifications can be received. However, we haven’t told the application, or the device, what to do when a notification is received. Let’s walk through each type of notification in turn.

Raw Notification

With the channel already established there is nothing else you need to do to configure the channel in order for notifications to be received into the application. However, in order for you to process the received notification you need to add an event handler to the HttpNotificationReceived event on the HttpNotificationChannel. Figure 4 illustrates an event handler for this event; the Body property on the Notification object is a Stream that your application can read from that represents the raw bytes that were sent via the Raw Notification.

Push Notifications Figure 1

Figure 1

Sending any type of notification is a matter of performing a HTTP POST operation to the ChannelUri of the application instance. You are required to set some additional headers and the payload has to be appropriately formatted, depending on the type of notification you’re sending. For a raw notification the payload is unstructured.

The following code establishes a new web request to the supplied channelUri, sets the appropriate headers, opens the request stream, writes the notificationMessage and then completes the request. This code also illustrates how you can parse the response to extract status information about the notification, the channel and the connected status of the device.

private static Guid SendNotification(byte[] notificationMessage, Uri channelUri, 
                                                                  int notificationType, string targetHeader = null) {
    var request = (HttpWebRequest)WebRequest.Create(channelUri);
    request.Method = "POST";
    request.Headers = new WebHeaderCollection();
    var messageId = Guid.NewGuid();
    request.Headers["X-MessageID"] = messageId.ToString();
    request.Headers["X-NotificationClass"] = notificationType.ToString();
    if (!string.IsNullOrEmpty(targetHeader)) {
        request.ContentType = "text/XML";
        request.Headers["X-WindowsPhone-Target"] = targetHeader;
    }

    request.BeginGetRequestStream((result) => {
        var req = result.AsyncState as HttpWebRequest;
        using (var requestStream = req.EndGetRequestStream(result)) {
            requestStream.Write(notificationMessage, 0,
                                notificationMessage.Length);
        }
        req.BeginGetResponse((responseResult) => {
            var responseRequest = result.AsyncState as HttpWebRequest;
            using (var response = responseRequest.EndGetResponse(responseResult))
            {
                string notificationStatus = response.Headers["X-NotificationStatus"];
                string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
                string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
            }
        }, req);
    }, request);

    return messageId;
}

Now all you need to do is to call the SendNotification method with the appropriate parameters. For a raw notification you would make the following call. The 3 indicates that the notification type is a raw notification that should be sent immediately.

private void SendRawClick(object sender, RoutedEventArgs e) {
    var msg = Encoding.UTF8.GetBytes("Hello World!");
    SendNotification(msg, Channel.ChannelUri, 3);
}

Note that in the case of a raw notification you do not need to specify the targetHeader property as this is only relevant to tile and toast notifications.

Toast Notification

As you will want toast notifications for your application to appear even when the user is not in your application you need to let the operating system know that it should accept toast notifications on behalf of your application. To do this you need to call the BindToShellToast method on the HttpNotificationChannel instance after you have called Open.

Channel = new HttpNotificationChannel(ChannelName);
Channel.ChannelUriUpdated += HttpChannelChannelUriUpdated;
Channel.Open();
Channel.BindToShellToast();

When the user is in your application and a toast message is sent to your application, rather than the usual toast interface, the message will be intercepted by your application, raising the ShellToastNotificationReceived event on the HttpNotificationChannel instance.

Channel.HttpNotificationReceived += HttpChannelHttpNotificationReceived;     
Channel.ShellToastNotificationReceived += HttpChannelShellToastNotificationReceived;

In the corresponding event handler you can parse the collection of returned strings. Toast notifications are made up of two strings with key values of “text1″ and “text2″.

private void HttpChannelShellToastNotificationReceived(object sender, NotificationEventArgs e) {
    var sb = new StringBuilder();
    foreach (var kvp in e.Collection) {
        sb.AppendLine(string.Format("Key:{0} Value:{1}", kvp.Key, kvp.Value));
    }
    this.Dispatcher.BeginInvoke(() => {
                                        MessageBox.Show(sb.ToString());
                                    });
}

Lastly, to send a toast notification you simply need to generate the appropriately formed XML and convert it to a byte array which becomes the notification payload.

private void SendToastClick(object sender, RoutedEventArgs e) {
    var messageTemplate = "<?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>";
    var message = string.Format(messageTemplate, "Hello", "World!");
    byte[] msg = Encoding.UTF8.GetBytes(message);
    SendNotification(msg, Channel.ChannelUri, 2,"toast");
}

In this case the 2 specified in the SendNotification method indicates a toast notification sent immediately. Note that you also need to specify the targetHeader parameter of “toast”.

Tile Notification

Like with toast notifications, in order for a tile notification to be handled by the device whilst the application is not running you need to inform the operating system. This is done with the BindToShellTile method. Note that since you can update a Live Tile with an background image from a remote server, the BindToShellTile method can accept a collection of domains as a parameter. Only background images from these domains (or local tiles packaged with the application) can be specified in a tile notification.

Channel = new HttpNotificationChannel(ChannelName);
Channel.ChannelUriUpdated += HttpChannelChannelUriUpdated;
Channel.Open();

var allowedDomains = new Collection<Uri>();
allowedDomains.Add(new Uri("Http://www.builttoroam.com"));
Channel.BindToShellTile(allowedDomains);

There are no events raised within the application when a tile notification is received, even if the application is running. As with toast notifications, sending a tile notification just requires the correct XML formatted message.

private void SendTileClick(object sender, RoutedEventArgs e) {
    var messageTemplate = "<?XML version="1.0" encoding="utf-8"?>" +
                                                    "<wp:Notification XMLns:wp="WPNotification">" +
                                                        "<wp:Tile>" +
                                                                "<wp:BackgroundImage>{0}</wp:BackgroundImage>" +
                                                                "<wp:Count>{1}</wp:Count>" +
                                                                "<wp:Title>{2}</wp:Title>" +
                                                            "</wp:Tile> " +
                                                        "</wp:Notification>";

    var message = string.Format(messageTemplate, "Background2.jpg", 5, "New Title");
    byte[] msg = Encoding.UTF8.GetBytes(message);
    SendNotification(msg, Channel.ChannelUri, 1, "token");
}

Note that the “1″ indicates that a tile notification should be sent immediately and that the targetHeader should be “token” (not “tile” as you would have expected). This particular tile update references a different image, Background2.jpg, that is packaged as Content within the application XAP file, a badge of 5 and a new title to be displayed, as shown in Figure 2.

Push Notifications Figure 2

Figure 2

In this post you’ve seen a very quick overview of setting up your application to receive notifications and then how you can send raw, toast and tile notifications to the device.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.