How to receive Toast Tile and Raw Notifications on Windows Phone 7

Overview:

This post details how to receive Toast, Tile and Raw Notifications on Windows Phone 7 using Visual Studio 2010 CTP April  Refresh.  It assumes that you have already created a new:

Background:

What are push notifications? There three types of notifications you can send to a Windows Phone 7 device:

  1. Tile – A tile is a visual, dynamic representation of application specific state within the quick launch area of the phone’s start experience.  You can control the Image, Text and Count (“Badge”) of a tile.
  2. Toast – Displays as an overlay onto the user’s current screen, a bit like an outlook email notification popup that you can press that launches you into the application.  You can control the title and text of a Toast notification.
  3. Raw – provides the ability for your application service to push data to the application while it is in the foreground without the need for your application to poll the appplication service.

Creating a Notification Channel:

  1. Expand the Properties folder in your windows phone application project and open WMAppManifiest.xml
    •  Ensure that your <App node has the Publisher=”YourPublisherName” property defined.
    • Ensure that your Capabilities node is defned.  oOf specific importance is the ID_CAP_PUSH_NOTIFICATION
         <Capabilities>
            <Capability Name="ID_CAP_NETWORKING" />
            <Capability Name="ID_CAP_LOCATION" />
            <Capability Name="ID_CAP_SENSORS" />
            <Capability Name="ID_CAP_MICROPHONE" />
            <Capability Name="ID_CAP_MEDIALIB" />
            <Capability Name="ID_CAP_GAMERSERVICES" />
            <Capability Name="ID_CAP_PHONEDIALER" />
            <Capability Name="ID_CAP_PUSH_NOTIFICATION" />
            <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
          </Capabilities>
  2. In the solution explorer right click on your windows phone application project and select Add a Reference… In the Add Reference window select Microsoft.Phone.Notification.dll and press Ok
  3. HttpNotificationChannel class, contained within the Microsoft.Phone.Notification namespace, is the key to creating a notification channel between the Microsoft Push Notification Service and the WP7 Push Client.   Before jumping into the full code  listing we will look at fundamental features of the the HttpNotificationChannel
    • HttpNotificationChannel _channel  = new HttpNotificationChannel(channelName, serviceName) – The Constructor accepts a channelName which is the identifying name of the notification channel and the serviceName which is the name of the cloud service that the notification channel is associated with
    • Static method HttpNotificationChannel.Find(string channelName) - Allows you to find a previously created notification channel.  This is particularly useful when the notification channel already exists and is not presently closed.
    • Instance property httpNotificationChannel.ChannelURI.  This is the unique URI identifying the current active notification channel
    • Instance method _channel.Open().  Opens the channel between the Push Client and Push Notification Service.  Note as documented in the April CTP the push client debugging has to wait for two minutes after boot of the emulator (or device) before using the APIs otherwise a NotificationChannelOpenException occurs
    • _channel.BindToShellEntryPoint(shellEntryPoint);  – Used to subscribe to Tile notifications the Image of the tile can be either a reference to a local resource image or a remote resource reference.  At the time of writing this post remote tile notifications are currently not working with the Microsoft Push Notification Service 
    •  _channel.BindToShellEntryPoint(); - Used to subscribe to Tile notification the Image of tile can only reference a local image resource . Note the local image must be within your project and have its Build Action set to content otherwise the tile will not display
    •  _channel.BindToShellNotification();  – Used to bind to Toast notifications that are shown when your application is not in the foreground
    •   _channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(channel_ChannelUriUpdated); - Subscribe to the ChannelUriUpdated event to capture the ChannelUri returned from the Microsoft Push Notification Service after Opening  your channel.  Note each time the application runs the NotificationChannel is not guaranteed to be identical to the previous launch therefore it is important that you utilise this handler to capture and submit your ChannelURI to your cloud service. 
    • _channel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(channel_HttpNotificationReceived); – Subscribe to the HttpNotificationReceived event to handle Raw Notifications while your application is in the foreground
    • _channel.ShellEntryPointNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellEntryPointNotificationReceived); - Subscribe to the ShellEntryPointNotificationReceived event to handle tile notifications sent while your application is in the foreground
    • _channel.ShellNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellNotificationReceived); - Subscribe to the ShellNotificationReceived event to handle toast notifications sent while your application is in the foreground
    •  _channel.ExceptionOccurred += new EventHandler<NotificationChannelExceptionEventArgs>(channel_ExceptionOccurred); – Subscribe to this event as your catch all for when something goes wrong within the HttpNotificationChannel
  4. Add a new class to your project called NotificationProvider.cs and paste in the following
    The Code:

       using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading;
    using System.Windows;
    using Microsoft.Phone.Notification;
    using Microsoft.Phone.Shell;
    
    namespace NickHarris.Net.Provider
    {
        public class NotificationProvider : IDisposable
        {
    
            private HttpNotificationChannel _channel;     �
            private string _channelName;
            private string _serviceName;    �
            private Timer _retrySubscribeTimer = null; //retry of 2 mins if just started emulator/rebooted device - April CTP issue
    
            public NotificationProvider(string channelName, string serviceName)
            {
                _channelName = channelName;
                _serviceName = serviceName;         �
            }
    
            public void Connect(object stateInfo)
            {  �
                Connect();
            }
    
            private void Connect(Action actionIfNotFound)
            {
                 try
                {
                    _channel = HttpNotificationChannel.Find(_channelName);
                }
                catch (NotificationChannelNotFoundException e)
                {
                    Debug.WriteLine(e.ToString());
                }
    
                if (_channel != null)
                {
                    if (_channel.ChannelUri != null)
                    {
                        SubscribeToChannelEvents();
                        RegisterChannel(_channel.ChannelUri);
                        SubscribeToNotifications();
                    }
                    else
                    {
                        _channel.UnbindToShellEntryPoint();
                        _channel.UnbindToShellNotification();
                        _channel.Close();
                        RetryChannelConnect();
                    }
                }
                else
                {
                    actionIfNotFound();
                }
            }
    
            public void Connect()
            {
                try
                {
                    Connect(() =>
                    {
                        _channel = new HttpNotificationChannel(_channelName, _serviceName);
                        SubscribeToChannelEvents();
                        _channel.Open();
                        SubscribeToNotifications();
                    });
    
                }
                catch (NotificationChannelOpenException ex)
                {
                    //2mins after restart - documented bug in April CTP
                    RetryChannelConnect();
                }
                catch (NotificationChannelExistsException e)
                {
                    //This exception occurs if the notification channel was previously created and is not closed.             �
                    Connect(() =>
                    {
                        RetryChannelConnect();
                    });
                }
            }
    
            private void SubscribeToNotifications()
            {
                try
                {
                    //Remote tiles - currently not working I think this is a MPNS issue
                    //ShellEntryPoint shellEntryPoint = new ShellEntryPoint();
                    //shellEntryPoint.RemoteImageUri = new Uri("http://www.nickharris.net/wp-content/uploads/2010/06/Background1.png", UriKind.Absolute);            �
                    //_channel.BindToShellEntryPoint(shellEntryPoint); // tile - remote
    
                    _channel.BindToShellEntryPoint(); // tile - local
                }
                catch (NotificationChannelExistsException)
                { } //do nothing - allready been subscribed to for current channel
    
                try
                {
                    _channel.BindToShellNotification(); // - toast
                }          �
                catch (NotificationChannelExistsException)
                { } //do nothing - allready been subscribed to for current channel
            }
    
            private void SubscribeToChannelEvents()
            {
                _channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(channel_ChannelUriUpdated); // channel URI returned from MPNS
                _channel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(channel_HttpNotificationReceived); // Raw
                _channel.ShellEntryPointNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellEntryPointNotificationReceived); // Tile
                _channel.ShellNotificationReceived += new EventHandler<NotificationEventArgs>(channel_ShellNotificationReceived); // Toast
                _channel.ExceptionOccurred += new EventHandler<NotificationChannelExceptionEventArgs>(channel_ExceptionOccurred);
            }
    
            void channel_ExceptionOccurred(object sender, NotificationChannelExceptionEventArgs e)
            {
                Debug.WriteLine(e.Exception.ToString());
                MessageBox.Show(e.Exception.ToString());
            }
    
            void channel_ShellNotificationReceived(object sender, NotificationEventArgs e)
            {
                if (e.Collection != null)
                {
                    Dictionary<string, string> collection = (Dictionary<string, string>)e.Collection;
                    System.Text.StringBuilder messageBuilder = new System.Text.StringBuilder();
                    foreach (string elementName in collection.Keys)
                    {
                        MessageBox.Show(string.Format("elementName:{0}, value:{1}", elementName, collection[elementName]));
                    }
                }
            }
    
            void channel_ShellEntryPointNotificationReceived(object sender, NotificationEventArgs e)
            {
                if (e.Collection != null)
                {
                    Dictionary<string, string> collection = (Dictionary<string, string>)e.Collection;
                    System.Text.StringBuilder messageBuilder = new System.Text.StringBuilder();
                    foreach (string elementName in collection.Keys)
                    {
                        MessageBox.Show(string.Format("elementName:{0}, value:{1}", elementName, collection[elementName]));
                    }
                }
            }
    
            void channel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
            {
                if (e.Notification.Body != null)
                {
                    using (System.IO.StreamReader reader = new System.IO.StreamReader(e.Notification.Body))
                    {
                        Debug.WriteLine(reader.ReadToEnd());
                    }
                }
            }
    
            void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
            {                         �
                RegisterChannel(e.ChannelUri);
            }
    
            private void RegisterChannel(Uri uri)
            {
                using (MyCloudServiceRegistrationProvider provider = new MyCloudServiceRegistrationProvider())
                {
                    provider.RegisterChannel(uri.ToString(), this.ActOnRegisterChannelComplete); �
                }
            }
    
            private void RetryChannelConnect()
            {
                if (_retrySubscribeTimer == null)
                    _retrySubscribeTimer = new Timer(new TimerCallback(this.Connect), null, 2 * 60 * 1000, Timeout.Infinite);
                else
                    _retrySubscribeTimer.Change(2 * 60 * 1000, -1);
            }
    
            public void ActOnRegisterChannelComplete(bool success)
            {
                if (!success)
                    RetryChannelConnect();//Try subscribe/find process to send channel again
            }
    
            public void Dispose()
            {
                //if (_channel != null)
                //{
                //    _channel.Dispose();
                //}
    
                if (_retrySubscribeTimer != null)
                    _retrySubscribeTimer.Dispose();
            }
        }
    
    // Note: I have performed some testing on the code provided and it works for toast,tile(local) and raw notifications.  I will post updates to this code as I find bugs.
    
    }
  5. I Subscribe to my channel notifications on Application_Startup within App.Xaml.cs
            private void Application_Startup(object sender, StartupEventArgs e)
            {
                provider = new NotificationProvider("yourchannelname", "yourcloudservice");
                provider.Connect();
            }
  6. Now all you need to do is learn How to send Tile, Toast and raw notifications to the Microsoft Push Notification Service for delivery to your windows phone 7 emulator/device

FREE Event – The Sydney Architecture User Group – Building Enterprise Web Applications

Re-posting the announcement for the SAUG here:

Title: Building Enterprise Web Applications
Presenter: Paul Glavich
Date/Time: Thursday 24/06/2010 06:30 PM
Where: Grace Hotel , Kiralee or Pinaroo Function Room 77 York st Sydney,NSW. 2000
Abstract
Enterprise web applications. Are they any different to other web applications? What kinds of patterns should we apply to these kinds of applications? What considerations should be taken into account when building these kinds of applications? Reporting, Auditing, performance, general architecture, client side considerations and more. This presentation is a follow on from Omar Besiso’s presentation on Enterprise thick client patterns and will answer all these questions and more. If you want to find out the answers to these questions and more details around building enhterprise savvy web applications, then this presentation will provide some value. Presenter Bio Paul Glavich is an ASP.NET MVP and a member of the ASPInsiders group with close links to the ASP.NET Team. Paul’s day job is working for Datacom as a solution architect, specialising in the web space but is currently involved in architecting a thick client WPF application. Paul has been working with .Net since its inception, has been in the industry for over 20 years and has written 3 books with the latest one on .NET Performance Testing and Optimization which is available as a free eBook or from Amazon in hardcopy.