Push Notifications

In this Windows Phone 7 Development article we are going to cover a very exciting and interesting concept; Push Notifications. We find these exciting and interesting for the pure fact that with one click of a button or a timed event, we can notify an number of users that have downloaded our app of whatever we like. The level of communication is limited only by how much you have space for on a Windows Phone 7 screen. Considering we have the HTC Titan X310e to play with, we are thinking we will be good for space.

We do want to be clear that for this coding example we will be coding the Windows Phone 7 side of the application but the file to communicate with devices with the application installed, or any application that you can get the right information from, we have quickly coded and put in a convenient location for our readers to install at their leisure. We may choose to do a separate post purely on the form we constructed but we would need enough interest so feel free to let us know. Anyways, look down below for the code and some explanations of what is going on!


// Settings.xaml

<phone:PhoneApplicationPage
x:Class="WP7LDBStorage.Settings"
xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation%22"> http://schemas.microsoft.com/winfx/2006/xaml/presentation"</a>
xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml%22"> http://schemas.microsoft.com/winfx/2006/xaml"</a>
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="<a href="http://schemas.microsoft.com/expression/blend/2008%22"> http://schemas.microsoft.com/expression/blend/2008"</a>
xmlns:mc="<a href="http://schemas.openxmlformats.org/markup-compatibility/2006%22">http://schemas.openxmlformats.org/markup-compatibility/2006"</a>
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True">

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="Client Contact Information" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="Settings" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<CheckBox Content="Live Tile Enabled" Height="72" HorizontalAlignment="Left" Margin="8,39,0,0" Name="cbLiveTile" VerticalAlignment="Top" Width="444" Click="cbLiveTile_Click"/>
<TextBlock HorizontalAlignment="Left" Margin="8,8,0,0" TextWrapping="Wrap" Text="Live Tile" VerticalAlignment="Top"/>
<TextBlock Margin="8,111,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="179"><Run Text="Toast Notif"/><Run Text="ications"/></TextBlock>
<Button Content="Create Channel" Margin="8,138,8,0" VerticalAlignment="Top" Name="btnCreateChannel" Click="btnCreateChannel_Click" />
</Grid>
</Grid>

<!--Sample code showing usage of ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="MenuItem 1"/>
<shell:ApplicationBarMenuItem Text="MenuItem 2"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->

</phone:PhoneApplicationPage>

In the above code we have an updated Settings page to include labels for all of the features we are adding. We have also added a button which we have given the text Create Channel and will use an event handler in the code below to accomplish what we need it to do.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.IO;
using System.IO.IsolatedStorage;
using Microsoft.Phone.Notification;
using System.Diagnostics;

// Directive for the data model
using WP7LDBStorage.Model;
using Microsoft.Phone.Tasks;

namespace WP7LDBStorage
{
public partial class Settings : PhoneApplicationPage
{
public Settings()
{
InitializeComponent();
IsolatedStorageFile directory = IsolatedStorageFile.GetUserStoreForApplication();
if (directory.FileExists("settings.txt"))
{
var textFound = "";
using (var myFileStream = new IsolatedStorageFileStream("settings.txt", FileMode.Open, IsolatedStorageFile.GetUserStoreForApplication()))
using (var reader = new StreamReader(myFileStream))
{
var text = reader.ReadToEnd();
textFound = text;
}

if (textFound == "1")
cbLiveTile.IsChecked = true;
else
cbLiveTile.IsChecked = false;
}
}

private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
// Return to the main page.
if (NavigationService.CanGoBack)
{
NavigationService.GoBack();
}
}

private void cbLiveTile_Click(object sender, RoutedEventArgs e)
{
if (cbLiveTile.IsChecked.Value)
{
IsolatedStorageFile directory = IsolatedStorageFile.GetUserStoreForApplication();
if (directory.FileExists("settings.txt"))
{
using (var myFilestream = new IsolatedStorageFileStream("settings.txt", FileMode.Truncate, IsolatedStorageFile.GetUserStoreForApplication()))
using (var writer = new StreamWriter(myFilestream))
{
writer.Write("1");
}
}

CreateLiveTile();
}
else
{
IsolatedStorageFile directory = IsolatedStorageFile.GetUserStoreForApplication();
if (directory.FileExists("settings.txt"))
{
using (var myFilestream = new IsolatedStorageFileStream("settings.txt", FileMode.Truncate, IsolatedStorageFile.GetUserStoreForApplication()))
using (var writer = new StreamWriter(myFilestream))
{
writer.Write("0");
}
}

ResetMainTile();
}
}

private void CreateLiveTile()
{
var appTile = ShellTile.ActiveTiles.First();

if (appTile != null)
{
var standardTile = new StandardTileData
{
Title = "Contact Info",
//BackgroundImage = new Uri("Images/SecondaryTileFrontIcon.jpg", UriKind.Relative),
Count = App.ViewModel.AllClientInfoItems.Count, // any number can go here, leaving this null shows NO number
BackTitle = App.ViewModel.AllClientInfoItems[App.ViewModel.AllClientInfoItems.Count-1].FirstName.ToUpper(),
//BackBackgroundImage = new Uri("Images/ApplicationTileIcon.jpg", UriKind.Relative),
BackContent = App.ViewModel.AllClientInfoItems[App.ViewModel.AllClientInfoItems.Count-1].LastName.ToUpper()
};

appTile.Update(standardTile);
}
}

private void ResetMainTile()
{
ShellTile tile = ShellTile.ActiveTiles.First();
StandardTileData data = new StandardTileData
{
BackBackgroundImage = new Uri("LinkThatDoesntGoAnywhere", UriKind.Relative),
BackContent = string.Empty,
Count = 0,
BackTitle = string.Empty
};
tile.Update(data);
}

All the way at the top of this block of code you will see our using statements. The new ones that we have add for push notifications are “using Microsoft.Phone.Notification;”, “using System.Diagnostics;” and “using Microsoft.Phone.Task;”. We are using Microsoft.Phone.Notification for the actual push notifications that are being sent to our phones. The System.Diagnostics is actually going to benefit the developer over the end user as it will allow us to display text to the output windows in testing phases such as the URL we are looking to generate. After we have everything fine tuned we can adapt the values we display in the output windows into a more tangible piece of data for the end user to work with. The code can be left in as the user will not be using our project and for the code to actually run you need to do it in debug mode. The last using statement we use is Microsoft.Phone.Task, which we use later on to compose an email to send to who ever you would like to be able to send you push notifications. All of the code under the using statements should be pretty straight forward from the previous tutorial on Live Tiles. We can now move onto the exciting code below that will implement push notifications.


private void btnCreateChannel_Click(object sender, RoutedEventArgs e)
{
SetupChannel();
}

private void SetupChannel()
{
HttpNotificationChannel httpChannel = null;
string channelName = "BW-PushNotificationCenter";

//if channel exists, retrieve existing channel
httpChannel = HttpNotificationChannel.Find(channelName);

if (httpChannel != null)
{
//If we cannot get Channel URI, then close the channel and reopen it
if (httpChannel.ChannelUri == null)
{
httpChannel.UnbindToShellToast();
httpChannel.Close();
SetupChannel();
return;
}
else
{
OnChannelUriChanged(httpChannel.ChannelUri);
}
BindToShell(httpChannel);
}
else
{
httpChannel = new HttpNotificationChannel(channelName);
httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);
httpChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived);
httpChannel.Open();
BindToShell(httpChannel);
}
}

Here we have an event handler for the Create Channel button we created earlier in our XAML code. This event handler references a method that is called SetupChannel which does all of the work for us. The first line of code within the SetupChannel is setting up a HttpNotificationChannel variable that is used to establish connection between the Push Notification service and the push client (our application). The next thing we have to do is distinguish our application from the rest of the other push notification clients by giving our application a channelName which will be a string object.

The next thing we need to do is see if the channel exists and we can use the HttpNotificationChannel.Find() function to do this. We then need to check and see if the variable we created is empty or not. If the channel is not empty but the uniform resource identifier (URI) is not reachable we want to unbind the toast notification subscription from the notification channel and rerun the SetupChannel method again. If it is reachable, we want to run a custom function called OnChannelUriChanged and pass in the channel URI to it. Finally we run another custom function called BindToShell and pass the HttpNotificationChannel object to it.

If the channel does not exist then we are going to instantiate the HttpNotificationChannel with channelName. After that is complete we are going to add the URI event handler to the httpChannel and add the event handler for when a toast notification is received. We then want send httpChannel to the custom function BindToShell.


private void OnChannelUriChanged(Uri value)
{
Debug.WriteLine("URI: " + value.ToString());

EmailComposeTask emailComposeTask = new EmailComposeTask();
emailComposeTask.To = "";
emailComposeTask.Body = value.ToString();
emailComposeTask.Subject = "URL For Push Notifications On Client Contacts";
emailComposeTask.Show();
}

Above, we have the method OnChannelUriChanged which will give us the ability to write the URL to the debug window of Visual Studio 2010. Alternatively, as you can see here we are making a EmailComposeTask object with emailComposeTask. This allows use to build an email that we can use to pass information along to ourselves. We make the URL the body of the text, give a standard Subject line to know what we are sending and make the email blank to let the user or developer decide where to send this information. The emailComposeTask.Show() method will actual combine all of the information and run the task on our phone. The code above is for developers as opposed to the end user as we could do without it and retrieve the URL another way but implementing the Email Task here is somewhat more convenient.


void httpChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
Debug.WriteLine("Toast Notification Message Received: ");
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)
{
Debug.WriteLine(string.Format("Key: {0}, Value:{1}\r\n", elementName, collection[elementName]));
}
}
});
}

Above is the code we are using to see if a toast notification has been received from the server through the httpChannel_ShellToastNotificationReceived method.We use the Dispatcher.BeginInvoke method to send asynchronous calls using the code below. We create a dictionary of two strings called collection which we make equal to NotificationEventArgs object after we cast it to a Dictionary object. We actually invoke this method more as a developer as it really does not benefit the user nor affect them. The end user will actually never see any of the code implemented above.


private static void BindToShell(HttpNotificationChannel httpChannel)
{
try
{
httpChannel.BindToShellToast();
}
catch (Exception)
{
}
}

In the above we have the BindToShell method which we are using to bind the toast notification subscription to the client. We use a try catch statement to catch any error that we may encounter and dismiss it so the user is unable to see it.


void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
//You get the new Uri (or maybe it's updated)
OnChannelUriChanged(e.ChannelUri);
}
}
}

The last bit of code is a method called httpChannel_ChannelUriUpdated. In this method we are using NotificationChannelUriEventArgs to pass the ChannelUri to the method OnChannelUriChanged. With this piece of code complete we have completed what is necessary on the Windows Phone 7 side of things to be able to receive push notifications.

With the Windows Phone 7 side coded you should now be able to code, in your own application, the ability to receive push notifications. Since we are not concentrating on the form code that gives us the ability to send push notifications, Binary Wasteland has done a favor and coded it and made it available for download. With the form all that is needed is to provide the URL and then give the Title and the Text that should appear on our Windows Phone. We have included some photos of out HTC Titan X310e and the form sending and receiving the push notifications below. Until the next development article for Windows Phone, Happy Hacking!

[nggallery id=4]

Share This