Thursday, July 28, 2011

Push notifications in Windows Phone 7.5 (Mango) -part 2

This post follows up from PART 1. So far, we’ve created our custom web service and registered our phone app with the service. Now let’s implement the main operation in our web service.




Implementing PushToSubscribedPhones

This operation will be invoked from an external app that needs to have push notifications send out to registered devices. Here we need to create a web request, and depending on the type of notification we want to send out, create the payload in accordance with the required schema for that type. Then we send the request out to MPNS to each registered device, through the channelUri we stored for each device in the subscribedPhones dictionary :
public void PushToSubscribedPhones(NotificationData data)
{
foreach (var entry in subscribedPhones)
{
var uri = entry.Value;
byte[] payload;
 
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "text/xml";
 
if (data.PushType == "toast")
{
payload = GetToastPayload(data.Title, data.JobId);
request.Headers.Add("X-WindowsPhone-Target", "toast");
request.Headers.Add("X-NotificationClass", "2");
}
 
else if (data.PushType == "tile")
{
payload = GetTilePayload(data);
 
request.Headers.Add("X-WindowsPhone-Target", "token");
request.Headers.Add("X-NotificationClass", "1");
}
 
else //raw
{
payload = Encoding.UTF8.GetBytes(myMessageData);
request.Headers.Add("X-NotificationClass", "3");
}
 
request.ContentLength = payload.Length;
 
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(payload, 0, payload.Length);
}
 
var response = (HttpWebResponse)request.GetResponse();
}
}
}

Here’s an example of the GetTilePayload method, that generates the message to be sent within a tile notification :


private byte[] GetTilePayload(NotificationData data)
{
string payload = string.Format(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Tile Id=\"/Views/JobDetailsView.xaml?id=" + data.JobId + "\">") +
"<wp:BackgroundImage>{2}</wp:BackgroundImage>" +
"<wp:Count>{1}</wp:Count>" +
"<wp:Title>{0}</wp:Title>" +
"<wp:BackBackgroundImage>{3}</wp:BackBackgroundImage>" +
"<wp:BackTitle>{4}</wp:BackTitle>" +
"<wp:BackContent>{5}</wp:BackContent>" +
"</wp:Tile> " +
"</wp:Notification>",
text, data.Count, data.TileUri, data.BackTileUri, data.BackTitle, data.BackContent);
 
return Encoding.UTF8.GetBytes(payload);
}

Note a couple things : first, the message’s components are provided by the calling app through the data parameter. Second, many of these elements are actually values that will be set on the properties of the target live tile, both for the front and back side of the tile. And third, notice the Id attribute that identifies the tile targeted by the notification – remember there may be more than one tiles pinned to the phone’s start menu.

When GetResponse() is called in the previous snippet, the notification is actually sent out to MPNS, which sends back a response. Depending on network, device and other factors, MPNS will then send the notification to the registered phones.



Invoke the web service from an external app

Now that your setup is complete, you can have an external caller invoke the operation to send out information to registered devices in the form of either a push, tile or raw notification. For example, within a WPF app you may write :


pushClient.PushToSubscribedPhones(
new NotificationData
{
PushType = "toast",
JobId = job.Id,
DriverId = driverId,
Title = "Job updated : "
});

This uses an instance of the web service client proxy class, which can be generated for you by Visual Studio using the Add Service Reference command. You call the PushSubscribedPhones operation in our service (you may want to do so asynchronously).  You create a NotificationData instance to specify the information you need to pass to the phones in the notification. Note this example sends a Toast, but you can also send a Tile notification and set the properties of the target tile :


var data = new NotificationData
{
PushType = "tile",
JobId = jobId,
Title = "Cancelled",
TileUri = "/Images/TileJobCancel.png",
BackTileUri = "/Images/TileJobCancelBack.png",
Count = 0,
BackTitle = "CANCELLED",
BackContent = "Unpin this job"
};


When the phone app receives the tile notification, any pinned tile will be updated with the values passed in the message.


So we’ve gone briefly through the push notification setup process involving our phone app, a custom service, MPNS, and possibly an external caller. We could also implement the service part in the cloud, as well as the calling app if we chose to.

In a later post, I’ll discuss possible failures and how to handle them.

If you’d like me to cover certain topics, feel free to send a comment.


Happy notifications!

Push notifications in Windows Phone 7.5 (Mango) -part 1

With push notifications, you can have your website or service send messages to an app running on a bunch of phone devices registered to receive the messages. These messages may trigger a toast popup on the phone, update a specific tile, or simply pass some raw data  to the phone app.

Push notifications work by creating a dedicated, one-on-one communication channel between the phone app and the Microsoft Push Notification Service (MPNS). Then, when your service or web app wants to send a notification to the phone, it sends it to the MPNS through a pre-determined URI, and MPNS will forward it to the phone as soon as it can.


image


In Mango, push notifications have gained in robustness. They are now more reliable, efficient, and performant. An example of reliability improvement is the revamped so-called TDET detection mechanism, which is the timeout period on a mobile network after which the data connection to your device is dropped for network efficiency. There is a wide range of mobile network configurations in this regard, which used to cause problems for push notifications delivery – push notification require a persistent channel to exist between MPNS and the device. Mango brings broader network compability in this respect.

As a result of the deep improvements, you now have up to 30 MPNS channels on a single device, vs. 5 previously (I think that was the number). In any case, each app can still have only one MPNS channel.
Here’s the basic process to implement push notifications in your app :

- Create a web app or service to let devices register and to send out notifications to them
- In the phone app, create an MPNS channel and then register the phone with web app
- In the phone app, bind to the channel to receive toast, tile and/or raw notifications
- In the web app, implement the operation to create and send notifications to the devices
- From an external client, call the web app’s send method, passing notification data to it

Let's look at each of these steps.




Create the web app or service
In your solution, create a new WCF application. In your service definition interface you can define 2 methods, one that phones will call to register with the service, and the other to send notifications to registered devices. For example  :
[ServiceContract]
public interface IMyPushService
{
 
[OperationContract]
void RegisterPhone(int driverId, string channelUri);
 
[OperationContract]
void PushToSubscribedPhones(NotificationData data);
 
}

Note that RegisterPhones takes a unique Id for the device and the URI that the channel URI that the device obtained from MPNS, as we’ll see. Its implementation can be as simple as storing the device’s channel Uri in a static dictionary :


static Dictionary<int, Uri> subscribedPhones = new Dictionary<int, Uri>();
 
public void RegisterPhone(int driverId, string channelUri)
{
subscribedPhones[driverId] = new Uri(channelUri, UriKind.Absolute);
}

PushToSubscribedPhones accepts a custom object that will contain the data we need to include in our notification messages. So NotificationData can be any class that exposes properties we need to send that data back and forth. Of course it needs to be decorated with the [DataContract] attribute.


[DataContract]
public class NotificationData
{
[DataMember] public string PushType;
[DataMember] public string Title;
[DataMember] public int Count;
[DataMember] public string TileUri;
[DataMember] public string BackTitle { get; set; }
[DataMember] public string BackContent { get; set; }
... }

We’ll come back to PushToSubscribedPhones in a moment.


Register the phone app with MPNS

When your phone application starts it needs to establish a communication channel with MPNS. It looks for an existing one from a previous execution (channels are persisted on the phone), and if none is found, it creates one  :


channel = HttpNotificationChannel.Find(channelName);
if (channel == null)
{
channel = new HttpNotificationChannel(channelName);
}

You hook up a handler for the ChannelUriUpdated event, and in the handler you can retrieve the channelUri asynchronously returned by MPNS and then register with the custom service through the RegisterPhone operation :


channel.ChannelUriUpdated += OnChannelUriUpdated;
 
void OnChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
pushClient.RegisterPhoneAsync(App.MyDriverId, e.ChannelUri.ToString());
}

Once you’ve hooked up the handler, you can call the Open() method on Channel if the channel is new, which will asynchronously request a channel Uri.

Bind to the channel to receive toast and/or tile notifications

If you want your phone app to receive toast and/or tile notifications, in the above handler you also need to call the BindToShellXXX methods accordingly :


if (!channel.IsShellTileBound)
channel.BindToShellTile();
 
if (!channel.IsShellToastBound)
channel.BindToShellToast();

Note that the channel you create will only be persisted on the device across app instances if you bind to it for toast or tile. If you don’t, it will be destroyed and you’ll need to create a new channel the next time you run the app.

In addition to the ChannelUriUpdated event, you may want to handle the ShellToastNotificationReceived event if you want your app to receive toasts even when the app is running – in which case toasts normally get discarded. You can also handle HttpNotificationReceived if you want your app to receive and handle raw notifications, which are pieces of custom data send through MPNS.

So at this point, we have our web service sketched out and our phone app registered with both MPNS and our service. The next step is to implement the PushToSubscribedPhones() operation in the service. I’m running out of time so I’ll do that in Part2.

Stay tuned !

Part 2 is now live

Saturday, July 16, 2011

Windows Phone 7.5 (Mango) dynamic live tiles

In Mango, an application can have several tiles associated wih it : the primary or “application'” tile, and as many secondary tiles as desired. Secondary tiles have the capability of taking the user to a specific area of the application through an associated deep link, as opposed to the main application page as with the primary tile. This is one example of the new deep linking capability Mango brings.

As in WP7, tiles are pinned or unpinned to the phone’s start menu by the user. All tiles have the ability to display information dynamically, such as a current game score or flight status information. Your application updates the displayed information by setting the tile’s title and count properties, and/or the tile’s background image.

In Mango, tiles are now 2-sided, which means they flip over at regular intervals when pinned. We can now also use the BackTitle, BackContent and BackBackgroundImage properties to display on the flip side.

To create a tile in your app, you first create a StandardTileData object to set the desired properties :
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("/Images/Red.jpg", UriKind.Relative),
Title = "Mary & John",
Count = 12,
BackTitle = "Just married",
BackContent = "Current score : 123",
BackBackgroundImage = new Uri("/Images/Blue.jpg", UriKind.Relative)
};

Then you call the static Create() method on the ShellTile class :

ShellTile.Create(new Uri("/NewlyWeds.xaml?TileID=2&date=Mary And John", UriKind.Relative), NewTileData);
Notice the argument passed to the Create method : When creating a secondary tile, you can specify the launch Uri that the app will receive when started from the tile. You can point the Uri to a specific page deep into the app, or simply use it to pass query string parameters into the app.

Before creating a tile, or if you want to update an existing tile, you may need to check wether the tile already exists and if so, retrieve the existing tile in order to update it. You can use the ActiveTiles collection property of the ShellTile class to do that. ActiveTiles always contains the application’s primary tile as well as any secondary tiles created by the application. To retrieve the application tile, just use FirstOrDefault() :

var apptile = ShellTile.ActiveTiles.FirstOrDefault();


To retrieve a specific secondary tile, you need to use a unique identifier for that tile. You can use the deep link Uri that was passed into the constructor when the tile was created :

DirectUri = "/View/Kite.xaml?id=fuel2011";
ShellTile.Create(new Uri(DirectUri, UriKind.Relative), tileData);

Now to retrieve the tile, use a LINQ query on the ActiveTiles property to test whether NavigationUri contains the unique ID :

var tile = ShellTile.ActiveTiles.SingleOrDefault(
t => t.NavigationUri.ToString().Contains("id=" + ParamId));

Note that I’m not using NavigationUri.Contains(DirectUri). The reason is that the NavigationUri property returns a URL encoded version of the deep link Uri that was passed in the constructor. If you set a breakpoint you’ll see that NavigationUri returns something like ‘file:///View/Kite.xaml%3Fid=fuel2011’. So NavigationUri.Contains(DirectUri) would return false for the tile we’re trying to retrieve.

Once you get ahold of the existing tile, you can easily update its properties :


var data = new StandardTileData
{
Title = "New Fuel 2011!",
Count = 2,
BackgroundImage = new Uri(picpath, UriKind.Relative),
BackContent = "This is the great new updated Fuel!",
BackTitle = "Check it out!",
BackBackgroundImage = new Uri("/Images/tilekiteback.png", UriKind.Relative)
};
 
tile.Update(data);

You can also remote a tile by calling the Delete() method on the tile.

So using ShellTile and StandardTileData, it’s very easy to create and manipulate both primary and secondary tiles in our app. In a later post I’ll discuss updating tiles on a schedule, using both the ShellTileSchedule APIs and background agents.

Happy tiling!

Friday, July 1, 2011

Windows Phone AppConnect Bing search extensibility (Apps aka Extras)

 
A couple of days ago the Mango Beta 2 SDK, aka Windows Phone 7.1, was released, along with a pre-release test version of the device OS. There were some great additions to the Beta 1 feature set, which I’ll be discussing over my next posts – stay tuned.
Today I want to focus on Bing integration in Windows Phone, also known as search extensibility, search extras, and officially now, AppConnect. What is that anyway ? AppConnect lets your app appear with relevant Bing search results on the phone.
Some Bing searches return what’s called “Quick Cards” in additional to traditional web results. There are Product, Place, and Movie Quick Cards, which contain detailed information about the item, such as descriptions, images, reviews, and prices. Quick Cards also contain an additional ‘Apps’ section that lists marketplace Windows Phone apps that are related to the card’s product, place or movie.
So if the user is doing a search on the phone for a specific product, place or movie, and a Quick Card comes up in the search results, and the quick card is in one of the Bing categories that you’ve associated with your app, then your app will appear listed in the Apps section of the Quick Card (formerly called the ‘Extras’ section, hence the extras term above). Of course, your application needs to be in the marketplace for this to happen.
If the user taps on your app’s title, if the app is already installed on the phone s/he will get taken to the specific page you configured in your app, and the Bing search parameters will be passed to the app – more on that later. If the app is not installed, the user will be able to download it from the marketplace.
The next question is, how do we set up our application so it’ll show up in relevant Bing searches ? The process is fairly simple :
1) Add some ‘extension’ elements to your app’s WMAppManifest.xml file to specify relevant Bing categories
2) Create an Extras.xml file that maps Bing categories to titles and descriptions that you want to be displayed in the Apps tab of the Bing quick card
3) Optionally, create a landing page in your app to retrieve the query string parameters passed from the Bing quik card when the user launches your app from that card
Configuring extension elements
You add an <extensions> element under the <app> element of the WMAppManifest file, and within it, as many <extension> elements as you want to specify the Bing categories your app should be associated with in searches, such as :
<Extension ExtensionName="Bing_Products_Sports_and_Outdoors" ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}" TaskID="_default" ExtraFile="Extensions\\Extras.xml" />

The list of possible extension names can be found here :
http://msdn.microsoft.com/en-us/library/hh202958(VS.92).aspx
The value of ConsumerID is fixed and indicates this is a Bing search extension.
ExtraFile specifies the location of the Extras.xml file.

In the beta release, to be able to test your app in the emulator, you should also add the beta extensions,  in this case :

<Extension ExtensionName="Products" 
ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}" 
TaskID="_default" 
ExtraFile="Extensions\\Extras.xml" />




This won’t be required in the release version of Windows Phone 7.5.

Create the extras file

The extras.xml file contains mapping between Bing categories or groups of categories, and strings you want to appear in the quick card to describe your app :

<ExtrasInfo>

<AppTitle>
<default>Kiteland</default>
</AppTitle>

<Consumer ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5661}">

<ExtensionInfo>
<Extensions>
<ExtensionName>Products</ExtensionName>
<ExtensionName>Bing_Products_Sports_and_Outdoors</ExtensionName>
</Extensions>
<CaptionString>
<default>Extensive kitesurfing equipment info</default>
</CaptionString>
</ExtensionInfo>

<ExtensionInfo>
<Extensions>
<ExtensionName>Bing_Products_Travel</ExtensionName>
</Extensions>
<CaptionString>
<default>All you need for your kite travels</default>
</CaptionString>
</ExtensionInfo>

</Consumer>
</ExtrasInfo>


In addition to specifying the app title that should appear in the quick card, you add one or more ExtensionInfo elements that each specify a caption to display for if the associated Bing categories are hit. For example, you may want the quick card to display “Kiteboarding equipment” if the search result is in the Bing Products Sports & Outoors category, and “Kiteboarding travel” if in the Bing Products Travel category.

Create a landing page

For a better user experience you may want the user to be taken to a specific page if s/he taps on your app title in the Apps tab of the quick card. To do that, create a landing page and retrieve and parse the Bing parameters which are automatically passed to the page when launched from the Bing quick card. These parameters are ProductName and Category for a product card, MovieName and Category for a movie card, and PlaceName, PlaceLatitude, PlaceLongitude, PlaceAddress and Category for a place card :


var prodName = NavigationContext.QueryString.TryGetValue("ProductName");
var bingCategory = NavigationContext.QueryString.TryGetValue("Category");

Your application can then perform some logic based on these passed parameters, such as retrieving the product, movie or place from a database and displaying some data.

Testing the app

Now I’ve found that testing these features can sometimes be tricky as it involves working with Bing searches. For this example, I’ll assume you’ve included the Bing_Products_Sports_and_Outdoors category in your list of extensions. If you use the emulator, remember to include the ‘Products’ beta extension as well.

First, deploy and run your app to the emulator or device, and then press the Search button to start a Bing search.  Now type “sporting goods” as a search phrase. At the top of the search results, you should see at least one Product card being displayed, above the Web heading.

image

Click on that card to enter the quik card. If everything is configured correctly, the card should have an Extras pivot with your app title displayed in it. Tapping on the title, should launch your app and take you to the landing page.

image   image  image



To test movies and places, you need to either have location enabled on your device, or if not, to specify a location in your search phrase, such as ‘movies Pittsburgh’ or ‘Starbucks New Orleans’. Remember, searches for places and movies are ‘local’ searches.

This example assumes you’ve included the ‘Places’ and ‘Movies’ beta extensions in you manifest file as well as ‘Products’ - you’d also need to add production Bing categories before submitting your app to the marketplace.

To test places, once the search results are displayed, bring up the Local pivot, and then select one of the places listed below the map. That will bring up the place quick card, and you can then flip to the App pivot of the card to see your app.

image  image

Tapping the app name will launch your app, but in this version, that will have the effect of detaching from the debugger.

Again, make sure both the beta extensions and relevant Bing extensions are specified in your manifest. Also make sure the extensions are mapped in the extras.xml file.

To search for a movie, you can search for something like “movies Philadelphia” (unless you’re using a device with location enabled, and you’re located somewhere in the US for example, in which case you can just type ‘movies’ or a movie name). Then click on the ‘Movies near Philadelphia PA’ section above the Web heading, and you’ll see a list of movies. Selecting a movie will open a movie card, and you can then flip to the App pivot.

image  image

  image  image

Again, make sure you list and map both the ‘Movies’ beta extension and the ‘Bing_Movies’ single production extension for movies.



So to recap, using AppConnect you can easily make your app stand out by making it more discoverable through Bing searches, thus providing the user with a richer experience on the phone.

Happy AppConnecting