Friday, December 17, 2010

Mobile Enterprise Application. Step 3: Registering For Push Notifications

This is a third post in a series. Previous posts:
 - Step 1: General Architecture
 - Step 2: Authentication

I briefly defined Push Notification Service in the previous post. Here we will take a closer look at implementing notifications. PNS has cloud-based infrastructure maintained by Microsoft; phone application registers with it and assumes a unique endpoint address. That address is sent to the server portion of our system which uses it to forward messages to PNS infrastructure which, in turns, forwards it to the phone. There are three different types of alerts that can be sent:

  1. Tile notifications. If an application is pinned to the start screen, its image (a.k.a. tile) can change in response to tile notification. We can either change the entire image or just display a number on the default tile.
  2. Toast notifications. These are essentially short text messages that are briefly displayed on top of the phone screen. If user touches the message, associated application opens up.
  3. Raw notifications. If previous two are used to communicate with the phone OS, this one is targeted at the phone application itself. Therefore, message contents and system response will be application-specific.
Check User Preferences
According to Microsoft certification requirements, users should be able to opt out of push notifications. The most straightforward approach is to store user preferences in the application setting.
   1:      public class AppSettings
   2:      {
   3:          private readonly IsolatedStorageSettings _isolatedStore;
   4:   
   5:          public bool CanUsePNS
   6:          {
   7:              get
   8:              {
   9:                  return GetValueOrDefault<bool>(Constants.Settings.CanUsePNSKey, Constants.Settings.CanUsePNSDefault);
  10:              }
  11:              set
  12:              {
  13:                  AddOrUpdateValue(Constants.Settings.CanUsePNSKey, value);
  14:                  Save();
  15:              }
  16:          }
  17:   
  18:          public AppSettings()
  19:          {
  20:              _isolatedStore = IsolatedStorageSettings.ApplicationSettings;
  21:          }
  22:   
  23:          public bool AddOrUpdateValue(string key, Object value)
  24:          {
  25:          }
  26:   
  27:          public TValueType GetValueOrDefault<TValueType>(string key, TValueType defaultValue)
  28:          {
  29:          }
  30:   
  31:          public void Save()
  32:          {
  33:              _isolatedStore.Save();
  34:          }
  35:      }


Register Push Channel.
This needs to be done when application starts and after user confirmed he or she wants to use PNS.
   1:          public void RegisterPushChannel()
   2:          {
   3:              if(!(new AppSettings()).CanUsePNS) return;
   4:   
   5:              _httpChannel = HttpNotificationChannel.Find(Constants.ChannelName);
   6:   
   7:              if (null != _httpChannel)
   8:              {
   9:                  SubscribeToChannelEvents();
  10:                  SubscribeToService();
  11:                  SubscribeToNotifications();
  12:              }
  13:              else
  14:              {
  15:                  _httpChannel = new HttpNotificationChannel(Constants.ChannelName, Constants.Channels.Service);
  16:                  SubscribeToChannelEvents();
  17:                  _httpChannel.Open();
  18:              }
  19:          }


Method SubscribeToChannelEvents simply adds application handlers to process various events raised by the HttpNotificationChannel object.

   1:          private static void SubscribeToChannelEvents()
   2:          {
   3:              // Register to UriUpdated event - occurs when channel successfully opens
   4:              _httpChannel.ChannelUriUpdated += new System.EventHandler<NotificationChannelUriEventArgs>(HttpChannelChannelUriUpdated);
   5:   
   6:              // Subscribe to raw notifications
   7:              _httpChannel.HttpNotificationReceived += new System.EventHandler<HttpNotificationEventArgs>(HttpChannelHttpNotificationReceived);
   8:   
   9:              // General error handling for push channel
  10:              _httpChannel.ErrorOccurred += new System.EventHandler<NotificationChannelErrorEventArgs>(HttpChannelErrorOccurred);
  11:   
  12:              // Subscribe to toast notifications
  13:              _httpChannel.ShellToastNotificationReceived += new System.EventHandler<NotificationEventArgs>(HttpChannelShellToastNotificationReceived);
  14:          }

Method SubscribeToNotifications binds channel notifications to Windows shell:
   1:          private static void SubscribeToNotifications()
   2:          {
   3:              if (!_httpChannel.IsShellToastBound)
   4:              {
   5:                  _httpChannel.BindToShellToast();
   6:              }
   7:              if (!_httpChannel.IsShellTileBound)
   8:              {
   9:                  _httpChannel.BindToShellTile();
  10:              }
  11:          }

Associate User and Channel URI
Method SubscribeToService needs to send unique endpoint URI (which can be accessed via _httpChannel.ChannelUri property) to the server portion of the system, e.g., by calling a web service. The service will associate the URI with the ID of the currently logged in user. This is an important consideration: since different users may be using the mobile application (or the same person may have different user accounts), but channel URI will always be the same. If we do not properly associate URI with the current user, he or she will be receiving other person's notifications. By the same rationale, when user logs off from the application, a web service call needs to be made to disassociate her from push notifications URI.