1. Problem
You want to build an application that uses push notification to indicate to the user that something has changed in the cloud.
2. Solution
You must use the Push Notification Service to indicate the change that has happened.
3. How It Works
The Microsoft Push
Notification Service in Windows Phone gives you the capability to send
information and updates to an application from a web service. This new
way of interacting with a service hides the lack of multitasking in
Windows Phone 7. Push notification works as shown in Figure 1.
Another good reason to use
push notification is the need to preserve battery power. If applications
are continuously polling to check for new notifications, the device's
battery will quickly run down.
Your web service sends a
notification to the Microsoft Push Notification Service, which routes
the notification to the user's device interfacing with push notification client that notify to your application that something new is ready do know.
NOTE
Your application
will have only one channel available to establish a connection to the
Push Notification Service. The user can activate a maximum of 15
applications that use push notification. If your application results in
exceeding these quotas, an "InvalidOperationException (Channel quota
exceeded)" error will be raised.
After your notification is
delivered, the Push Notification Service sends a response to your
service. But remember that you will not receive a confirmation that your
notification was delivered.
There are three types of push notification that you can use: tile, toast, and raw.
The details are as follows:
Tile notification: Every application has a tile (a dynamic representation of the application) that is displayed on the start screen if the user chooses this option. Figure 2
shows, inside the rectangle, the default settings that your application
will use for tile notification. A tile is composed of the following
three elements, which you can update:
Count: An integer that can assume a value between 1 and 99 and that is displayed on the tile in a circle image
Title: A string set during development (see Figure 3)
Background: The background image contained in your project (see Figure 4)
NOTE
The title must fit on a single line of text. If you don't set this value, will be displayed the already defined title.
NOTE
If no Count values are set,
nothing will be shown. If you want to reset the count in the tile, you
must use 0 (zero) as the value.
Toast notification:
This consists of a message shown in the top part of the screen and
composed of a title and a subtitle. These two properties are simply
strings, with the title having a bold weight and the subtitle appearing
as normal text. You can see an example of toast notification when you
receive a text message, and you will havethe name of the person
contacting you as the title and the text as the subtitle.
Raw notification: This notification is sent directly to your application and can be maximum of 1 KB.
NOTE
If you send a raw notification, and your app is not running, the Push Notification Service will discard your notification.
4. The Code
Create a new solution, naming it WP7Recipe0904 Push Notification.
The first thing to do is to define a constant as a string in the code,
which identifies the channel that your application will use in the
future.
Then in App.xaml.cs, you write this:
...
//Change this value with your channel name
private const string channelName = "NotificationTest";
...
Now you have the channel name, but you're missing the most important object: HttpNotificationChannel. So you declare it:
...
HttpNotificationChannel notificationChannel;
...
HttpNotificationChannel is the class that supports (on the client side) the process of notification to the user, as you can see in Figure 9-10. This class puts at your disposal several methods, the first of which is Find. The Find method is used to check whether any notification channels with the name that you have set (channelName)
already exist on the device. and if exist returns it, This method is
very important for controlling the maximum number of channels that can
be created on a device. Also in figure 5, you can see that HttpNotificationChannel doesn't have a parameterless constructor, because you need to specify at least the channel name.
Now let's take a look at the
method to call inside the constructor of the app to see the others
important properties and events to subscribe to:
...
private void initNofiticationChannel()
{
try
{
notificationChannel = HttpNotificationChannel.Find(channelName);
if (notificationChannel == null)
{
notificationChannel = new HttpNotificationChannel(channelName);
notificationChannel.ErrorOccurred += new
EventHandler<NotificationChannelErrorEventArgs>(
notificationChannel_ErrorOccurred);
notificationChannel.ChannelUriUpdated += new
EventHandler<NotificationChannelUriEventArgs>(
notificationChannel_ChannelUriUpdated);
notificationChannel.Open();
}
if (!notificationChannel.IsShellTileBound)
notificationChannel.BindToShellTile();
if (!notificationChannel.IsShellToastBound)
notificationChannel.BindToShellToast();
notificationChannel.HttpNotificationReceived += new
EventHandler<HttpNotificationEventArgs>(
notificationChannel_HttpNotificationReceived);
notificationChannel.ShellToastNotificationReceived += new
EventHandler<NotificationEventArgs>(
notificationChannel_ShellToastNotificationReceived);
}
catch (InvalidOperationException ex)
{
//notify the user of this problem
MessageBox.Show("If you want to use all funcionality of this application,
you must disable notification in others app,
because you have too many channels opened");
//disable Push Notification in your application
}
}
As you can see, you call the Find method to determine whether a channel already exists—of course, because without a channel, nothing works.
If the channel is not present,
you need to create one with the name that you decided to enter into the
constant. (Keep in mind that it is not critical to insert the name of
the channel in the constant, but it is a good way to avoid common
errors.) notificationChannel object initialization does not means that you opened it
Subscribe to events that might be raised during the opening of the first
ErrorOccurred occurs whenever there is a problem in the use of class (and also later during the reception of notifications could be raised)
ChannelUriUpdated
will be raised after you receive the URI on Microsoft Push Notification
Service of your channel that will allow you to send notifications
directly on your device.
Then open the channel. This may trigger the exception that we mentioned earlier, InvalidOperationException.
This tells you that there are already too many applications that use
push notification on the device. If you don't have permission to use
notification in your app, you may want to notify the user (for example,
with a message box) that having too many open channels on the device
blocks the full use of your application (especially if your application
is meant to make wide use of notifications).
In this example, you bind the
channel directly to the notifications (tile and toast). However,
remember that to use toast notification, you must allow the user to turn
them off from the control panel of your application. After this, you subscribe to the two events, HttpNotificationReceived and ShelToastNotificationReceived.
The first will be raised when
a raw notification is received. The second will be raised when the
application receives a toast notification. (This event will occur only
if the application is running in the foreground.)
Now take a look at the ChannelUriUpdated
event handler. In this example, you write at the debug console the
channel URI, but you have to add the logic to notify your web service of
the address to which the push notification is sent.
...
void notificationChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
Debug.WriteLine(e.ChannelUri);
//this is the code to notify your webservice ofthe Uri of the channel
}
The channelUri object will be something like this:
http://db3.notify.live.net/throttledthirdparty/01.00/XXXXXXXXXXXXXXXXX...
You would replace all the Xs with the identification of your channel.
The concept is simple: you
have received this channel, and now your service can send notifications.
However, because it is not our aim to show you how to make a service,
we will instead show you how to build a small test application to send
notifications to the device.
Open Visual Studio. Create a desktop application and call it SendNotificationApplication. In the XAML of the main page, create the following structure:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d=http://schemas.microsoft.com/expression/blend/2008
xmlns:mc=http://schemas.openxmlformats.org/markup-compatibility/2006
mc:Ignorable="d"
x:Class="SendNotificationApplication.MainWindow"
Title="Main Window">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Notification Uri" />
<TextBox x:Name="NotificationUriTextBox" Grid.Column="1"/>
<TextBlock Text="Text 1 " Grid.Row="1" />
<TextBox x:Name="FirtsRowTextBox" Grid.Row="1" Grid.Column="1" />
<TextBlock Text="Text 2 " Grid.Row="2" />
<TextBox x:Name="SecondRowTextBox" Grid.Row="2" Grid.Column="1" />
<TextBlock Text="Background" Grid.Row="4" />
<TextBox x:Name="BackgroundTextBox" Grid.Row="4" Grid.Column="1" />
<TextBlock Text="Count" Grid.Row="5" />
<TextBox x:Name="CountTextBox" Grid.Row="5" Grid.Column="1" />
<TextBlock Text="Title" Grid.Row="6" />
<TextBox x:Name="TitleTextBox" Grid.Row="6" Grid.Column="1" />
<Button x:Name="SendTileNotificationButton" Grid.Row="7" Grid.ColumnSpan="2"
Content="Send Tile Notification"
Click="SendTileNotificationButton_Click" />
<Button x:Name="SendToastNotificationButton" Grid.Row="3" Grid.ColumnSpan="2"
Content="Send Toast Notification"
Click="SendToastNotificationButton_Click" />
<TextBlock x:Name="ResponseTextBox" Grid.Row="8" TextAlignment="Left"
Grid.ColumnSpan="2" />
</Grid>
</Window>
That will display something like Figure 6. Then you write the two event handlers for the two buttons. The first event handler is SendTileNotificationButton_Click,
which takes the subscription URI from the text box and then creates the
request. You must always remember that you can't send a notification
via the GET method, but only with POST.
After this, the next step is to add headers to your request. Optionally, you can add an X-MessageID
header that uniquely identifies your notification message, and will be
returned in a response. Then, if something goes wrong with a message,
you can associate the request with the response to know what's happened
at a specific request. If a response doesn't satisfy your requirements,
you can can stop it from being sent to the user.
X-NotificationClass can
assume three values: 1, 11, and 21. With 1 as the value, your
notification will be sent immediately; with 11, you will have a delay of
450 seconds; and with 21, you will have a delay of 900 seconds.
private void SendTileNotificationButton_Click(object sender, RoutedEventArgs e)
{
string subscriptionUri = this.NotificationUriTextBox.Text;
HttpWebRequest sendNotificationRequest =
(HttpWebRequest)WebRequest.Create(subscriptionUri);
sendNotificationRequest.Method = "POST";
sendNotificationRequest.Headers.Add("X-MessageID", Guid.NewGuid().ToString());
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "token");
sendNotificationRequest.Headers.Add("X-NotificationClass", "1");
string tileMessage = "<?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>";
byte[] notificationMessage = new System.Text.UTF8Encoding().GetBytes(
string.Format(tileMessage,
BackgroundTextBox.Text,
CountTextBox.Text,
TitleTextBox.Text));
// Sets the web request content length.
sendNotificationRequest.ContentLength = notificationMessage.Length;
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
HttpWebResponse response = (HttpWebResponse)
sendNotificationRequest.GetResponse();
StringBuilder sb = new StringBuilder();
foreach (var item in response.Headers)
{
sb.AppendLine(string.Format("{0}-->{1}",
item.ToString(),
response.Headers[item.ToString()]));
}
ResponseTextBox.Text = sb.ToString();
}
On the other hand, you have the
code to send a toast notification that appears largely similar to the
code for the tile notification. The main difference is in the format of
the XML that makes up the notification.
In this case, X-NotificationClass can take the value 2, 12, or22, reflecting a delay in sending the notification of 0, 450, and 900 seconds, respectively.
...
private void SendToastNotificationButton_Click(object sender, RoutedEventArgs e)
{
string subscriptionUri = this.NotificationUriTextBox.Text;
HttpWebRequest sendNotificationRequest = (HttpWebRequest)
WebRequest.Create(subscriptionUri);
sendNotificationRequest.Method = "POST";
sendNotificationRequest.Headers.Add("X-MessageID", Guid.NewGuid().ToString());
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");
sendNotificationRequest.Headers.Add("X-NotificationClass", "2");
string toastMessage = "<?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>";
byte[] notificationMessage = new System.Text.UTF8Encoding().GetBytes(
string.Format(toastMessage,
FirstRowTextBox.Text,
SecondRowTextBox.Text ));
sendNotificationRequest.ContentLength = notificationMessage.Length;
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(notificationMessage, 0, notificationMessage.Length);
}
HttpWebResponse response = (HttpWebResponse)
sendNotificationRequest.GetResponse();
StringBuilder sb = new StringBuilder();
foreach (var item in response.Headers)
{
sb.AppendLine(string.Format("{0}-->{1}",
item.ToString(),
response.Headers[item.ToString()]));
}
ResponseTextBox.Text = sb.ToString();
}
You can see the effects of sending these two notification in Figure 7
5. Usage
Open these two projects
and start deploying the application to the Windows Phone Emulator. Get
the notification URI from the debug console and then use it in SendNotificationApplication. Remember that to see the toast notification, you must have your application running in the background.