Sunday, June 3, 2012

Windows 8 push notifications


This post is a brief overview of using push notifications in a Metro app. Push notifications let you create a web service that will be able to independently send tile, toast or raw notifications to your Metro app over Http using a preconfigured communication channel between the app and the service. So your app doesn’t need to poll the service regularly to check for updates, instead, the service can send updates to your app whenever new information is available. This is done through the Microsoft Windows Notification Service or WNS, which is an always-on Microsoft-managed service whose sole purpose is to channel billions of small messages from third-party callers to registered Metro apps in an efficient and dependable manner. Let’s see how this works in a nutshell.

image


1) Your Metro app requests from WNS that a channel be created between itself and WNS. WNS returns a unique channel Uri that represents that channel to the app.

2) The app forwards the channel Uri to your custom service – which you set up ahead of time. Your service stores the uri with the id of the device running the app. When the time comes, your service will use that uri to send a tile or toast notification to the registered devices through WNS.

3) Your service authenticates with WNS (one-time operation) by requesting and storing an authentication token returned by WNS. When the service is ready to send a toast or tile notifications to registered apps, it will send an HTTP  message to WNS including the stored auth token, so that WNS accepts the message.

4) Your service is now ready to send a notification to registered devices : it creates an XML message matching the tile or toast required schema, and sends it to each uris associated with the registered devices. WNS receives each authenticated message and forwards it to the corresponding device. As a result, the app’s tile is updated or a toast is raised on the device.

So first, you request a channel from your Metro app, using the PushNotificationChannelManager class :
   1: channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

Then you can forward the uri to your service, which has a register operation that accepts a Uri along with some unique identifier for the device running the Metro app :


   1: var data = new PushClientData
   2:             {
   3:                 CustomerId = App.Id,
   4:                 ChannelUri = channel.Uri.ToString()
   5:             };
   6:  
   7: // TODO : insert data into request content
   8:  
   9: //Send request
  10: var response = await client.PostAsync(uri, content);

The service operation may look something like this :


   1: public void RegisterApp(PushClientData app)
   2: {
   3:     //TODO : verify Uri is a valid WNS channel Uri
   4:  
   5:     registeredClientsDictionary[app.Id] = new Uri(app.ChannelUri);
   6: }

When your service runs for the first time, it needs to authenticate with WNS by retrieving an authentication token :


   1: protected void GetAccessToken()
   2: {
   3:     var urlEncodedSid = HttpUtility.UrlEncode(sid);
   4:     var urlEncodedSecret = HttpUtility.UrlEncode(secret);
   5:  
   6:     var body =
   7:       String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com",
   8:       urlEncodedSid, urlEncodedSecret);
   9:  
  10:     var client = new WebClient();
  11:     client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
  12:  
  13:     string response = client.UploadString(new Uri(authUri), body);
  14:     var oAuthToken = GetOAuthTokenFromJson(response);
  15:     this.accessToken = oAuthToken.AccessToken;
  16: }

The sid and secret key can currently be obtained by registering your Metro app in the Windows Push Notifications & Live Connect portal (although this may change later on in the Metro ecosystem). The GetOAuthTokenFromJson helper simply extracts the token from the JSON response returned by WNS.

Now your Metro app is all set up, and your cloud service is authenticated with WNS. The service has stored the channel Uris and Ids of each Metro device running your app and registered with WNS to receive notifications. The service can now send a tile or toast notification to the registered devices through WNS using the stored channel Uris. First, let’s create the notification message the service will send WNS :


   1: private string GetTilePayload(MyDataClass data, string tileId = "tile1")
   2: {
   3:    var wideTemplateName = "TileWidePeekImage01";
   4:  
   5:    tile = string.Format("<tile><visual lang=\"en-US\">" +
   6:                            "<binding template=\"{0}\">" +
   7:                            "<image id=\"1\" src=\"{1}\"/>" +
   8:                            "<text id=\"1\">{2}</text>" +
   9:                            "<text id=\"2\">{3}</text>" +
  10:                            "</binding>", 
  11:                            wideTemplateName, data.Photo, data.Fname + " " + data.Lname, data.Comments);
  12:  
  13:    return tile;
  14: }

Here the service creates the string payload matching the required schema for a tile notification. See this post for more info. If instead, you want the service to send a toast, here’s an example :


   1: private string GetToastPayload(MyDataClass data)
   2: {
   3:     var templateName = "ToastImageAndText02";
   4:     var toast = string.Format("<toast><visual lang=\"en-US\">" +
   5:                             "<binding template=\"{3}\">" +
   6:                             "<image id=\"1\" src=\"{0}\"/>" +
   7:                             "<text id=\"1\">{1}</text>" +
   8:                             "<text id=\"2\">{2}</text>" +
   9:                             "</binding></visual></toast>",
  10:                             data.Photo, data.Fname + " " + data.Lname, data.Comments, templateName);
  11:     return toast;
  12: }

Once your service has built the notification payload, it needs to build the WNS request :


   1: protected string SendNotification(Uri uri, string payload, string type)
   2: {
   3:     byte[] content = Encoding.UTF8.GetBytes(payload);
   4:     string statusCode = "";
   5:  
   6:     var request = HttpWebRequest.Create(uri) as HttpWebRequest;
   7:     request.Method = "POST";
   8:  
   9:     request.Headers.Add("X-WNS-Type", type);
  10:     request.Headers.Add("Authorization", string.Format("Bearer {0}", accessToken));
  11:  
  12:     request.BeginGetRequestStream(result =>
  13:     {
  14:         var requestStream = request.EndGetRequestStream(result);
  15:         requestStream.Write(content, 0, content.Length);
  16:         request.BeginGetResponse(result2 =>
  17:         {
  18:             var response = request.EndGetResponse(result2) as HttpWebResponse;
  19:             statusCode = response.StatusCode.ToString();
  20:         }, null);
  21:     }, null);
  22:  
  23:     return statusCode;
  24: }

As you can see, the service builds a POST request that includes the authentication token in a header and the xml payload we just created in the body. In this example the service creates and sends the request asynchronously for better scalability, although yoy may choose to do so synchronously depending on your scenario.

Note that the authentication token has a time limited validity, so you should be prepared for the case where WNS returns an expired token message and rejects the notification message :


   1: try
   2: {
   3:     var response = request.EndGetResponse(result2) as HttpWebResponse;
   4:     statusCode = response.StatusCode.ToString();
   5: }
   6: catch (WebException ex)
   7: {
   8:     string authHeader = ex.Response.Headers["WWW-Authenticate"];
   9:     if (authHeader.Contains("Token expired"))
  10:     {
  11:         GetAccessToken();
  12:         statusCode = SendNotification(uri, xml, type);
  13:     }
  14:     else
  15:     {
  16:         //return ex.Message;
  17:     }
  18: }

Here I’ve wrapped the code that sends the request and fetches the WNS response in a try-catch block, and if the response indicates an expired token, the code goes out and fetches a new token and then resends the notification to WNS.

So when this method is invoked in your service by some external caller, such as an arbitrary service client, the notification request will get sent out to WNS, and WNS will forward it to all registered devices running your Metro app, which will in turn receive the message and consequently update the app tile or raise a toast on the device.

Note that the app may also want to run some code on the device when a push message is received :


   1: private void InterceptNotification()
   2:         {
   3:             channel.PushNotificationReceived += (s, e) =>
   4:             {
   5:                 switch (e.NotificationType)
   6:                 {
   7:                     case PushNotificationType.Toast:
   8:                         toast = e.ToastNotification; break;
   9:                     case PushNotificationType.Tile:
  10:                         tile = e.TileNotification; break;
  11:                     case PushNotificationType.Badge:
  12:                         badge = e.BadgeNotification; break;
  13:                 }
  14:                 //app-specific code ...
  15:             };
  16:         }



So to recap, you can use push notifications to have a custom cloud service send tile, toast or other kinds of messages to your Metro app. This requires quite a bit of setup involving Microsoft’s Windows Notification Service. You establish communication channels between your Metro app and WNS, between your app and your service, and between your service and WNS. As a result your app can benefit from the push model and from the efficiency and dependability of WNS to receive external notifications without polling or similar actions.

Hope you enjoyed this post!

Sunday, May 27, 2012

Windows 8 live tiles


In Windows 8, live tiles are an extension of your Metro app. It’s not just an icon sitting on the Start menu which the user can tap to launch your app. It’s also a live window into your app. When it’s running, your app can update its main tile with new content using tile notifications. It can also allow the user to pin or unpin secondary tiles that point to specific subareas of your app, such as a specific product. When your app is not running, the tiles associated with it (both main and secondary) may be updated by a scheduled notification or by a push notification sent by an external service in the cloud. In this post, we look at the basics of tiles, how to configure your app’s main tile and update it using local tile notifications while the app is running.
Your Metro app has a default (main) tile that you initially configure in the manifest. Each tile may be displayed in the start menu in a square format or a wide format, based on the user’s choice. You may or may not want your app to support the wide format, and it’s recommended that you only do if you need the extra space to displaying the info the tile needs to show. In other words, only use a wide tile if there’s enough stuff to put in it.
So when you configure your initial app tile in the manifest, you can specify a small tile image (150 x 150 pixels), a wide image (150 x 310), an app name to display on the tile, and in which tile sizes you want the name to appear. You can also specify a background color for the tile – that will also be used for the splash screen and system UI elements such as dialog buttons.
image
Once you deploy the app, if you go to the Start menu you’ll find the app’s main tile pinned using the images and content your specified. You can switch back and forth between the square version and the wide version – assuming you specified a wide image.
image                         image
Now you have your default tile configured and deployed. Suppose your app need to update its main tile with some text and images relating to what the app does. This is how you do it :
- Choose one of the tile layout templates available in the platform
- Get the associated xml template
- Insert your app-specific content (text and images) into the xml
- Create a tile notification based on the xml template with content
- Create a tile updater for your main tile
- Pass the tile notification you created to the Update method of the updater
Choose the tile layout and get the xml template
The available layouts are listed here.
Suppose you picked TileWideSmallImageAndText04, which looks like this :
TileWideSmallImageAndText04 example
It has one small square image and 2 text strings, one for title and the other wrapped over 3 lines. You retrieve the xml for that layout :
   1: var templateType = TileTemplateType.TileWideSmallImageAndText04;
   2: var xml = TileUpdateManager.GetTemplateContent(templateType);

Insert your content into the XML

Now, retrieve both the text nodes and images nodes, and insert the content you want to show in the tile. This template has 2 text strings and 1 image, so I’m inserting each of those using the XML DOM API :


   1: var textNodes = xml.GetElementsByTagName("text");
   2: textNodes[0].AppendChild(xml.CreateTextNode("You have a new message"));
   3: textNodes[1].AppendChild(xml.CreateTextNode("This text should wrap on a max of a maximum of 3 lines bla bla bla"));
   4:  
   5: var imageNodes = xml.GetElementsByTagName("image");
   6: var elt = (XmlElement)imageNodes[0];
   7: elt.SetAttribute("src", "Images/someImage.jpg");



Create a tile notification

Just pass the xml to a new notification instance :


   1: var tile = new TileNotification(xml);

Create a tile updater and update the tile


   1: var tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
   2: tileUpdater.Update(tile);





That will update the wide version of the app’s tile. Now suppose you also want to update the square version at the same time. In that case, you need to retrieve a square tile template, insert your content in the xml just like you’ve done for the wide version, and extract the ‘binding’ node of the xml, and append that node to the ‘visual’ node of the wide tile template xml you worked with above :


   1: templateType = TileTemplateType.TileSquarePeekImageAndText02; //flips between square image and 2 pieces of text (title + smaller)
   2: var squareXml = TileUpdateManager.GetTemplateContent(templateType);
   3:  
   4: textNodes = squareXml.GetElementsByTagName("text");
   5: textNodes[0].AppendChild(squareXml.CreateTextNode("Some title"));
   6: textNodes[1].AppendChild(squareXml.CreateTextNode("Some content"));
   7:  
   8: imageNodes = squareXml.GetElementsByTagName("image");
   9: ((XmlElement)imageNodes[0]).SetAttribute("src", "http://s.pluralsight.com/mn/img/au/lg/yacine-khammal-v2.jpg");
  10:  
  11: //Retrieve 'binding' element of square tile and append it to top level 'visual' element of parent wide tile
  12: var bindingNode = xml.ImportNode(squareXml.GetElementsByTagName("binding")[0], true);
  13: xml.GetElementsByTagName("visual")[0].AppendChild(bindingNode);

Of course the above code must execute before the call to the TileNotification constructor.

So what kind of scenario is this useful for ? Suppose you have a photo app for example, you may want the tile to show the last photo the user looked at in the app. So in the part of your app where the user opens a photo, you also execute code to send a tile notification with the photo as image and whatever title or description is associated with the photo as text. So when the user is on the start screen they will see that last viewed photo and title in the app tile.

In this post, I briefly discussed the application tile and how we can update it through local tile notifications. There is much more going on with tiles in Windows 8, including badge notifications, secondary tile, scheduled and push notifications. But that will be for another post.

Happy tiling

Saturday, May 19, 2012

Windows 8 Metro background transfers (video)


A picture is worth a thousand words, so here’s a basic video about how to download a file in the background and track download progress in a Windows 8 Metro app. This is only scratching the surface of the Background Transfer API, there’s much more such as cancellation, pause & resume, app relaunch support etc.  Check out my upcoming Win8 Connected Apps course on Pluralsight.com (due with the RC release of Win8).
In this how to we use the BackgroundDownloader API and the .NET 4.5 async pattern and Win8’s native async interfaces to register a progress callback for the background transfer.

How to download a file in the background and track progress in a Win8 Metro app

Sunday, March 18, 2012

File searching and filtering


In my last post, I discussed accessing and displaying file data from your Metro app, and grouping files using the pivot API. Let me follow up on that with a discussion of basic file filtering using the search API. So in our photo browser app, suppose we’d like the user filter photos by their own rating. In the Windows OS, the user can right click on a picture and set a personal rating using the Rating property :
image
This is the property that we’re going to use for our filtering example. So we’ll assume the user has at least some pictures rated using the above property settings in her file system.
Here’s what we want to achieve :
image
The buttons in the top row match each distinct rating value the user has assigned to her pics. So we need to generate these buttons. Then, when the user clicks one of them, the list will refresh to only show pics matching the selected rating value.
Let’s start with the buttons :
   1: private async void CreateRatingButtons()
   2: {
   3:     var pivot = CommonFolderQuery.GroupByRating;
   4:     var folders = await library.GetFoldersAsync(pivot);
   5:  
   6:     var ratingValues = folders.Cast<StorageFolder>().Select(
   7:         f => new Rating { Name = f.Name }).OrderBy(r => r.Name);
   8:     Ratings = new ObservableCollection<Rating>(ratingValues);
   9:     DefaultViewModel["Ratings"] = Ratings;
  10: }

You can see our old friend the pivot, which we first use to group the files by rating.  As in the previous post, we generate a list of virtual folders, each representing a group of pictures matching a specific rating value. Then we just retrieve the vfolder name which is the rating value itself. At this point, our goal is just to get the list of rating values assigned by the user in her file system. We store them in a simple data model class in an observable collection to make data binding easier. Then we bind an items control to the collection to generate the list of buttons :


   1: <ItemsControl x:Name="icRatings"
   2:               ItemsSource="{Binding Ratings}">
   3:     <ItemsControl.ItemsPanel>
   4:         <ItemsPanelTemplate>
   5:             <StackPanel Orientation="Horizontal" />
   6:         </ItemsPanelTemplate>
   7:     </ItemsControl.ItemsPanel>
   8:     <ItemsControl.ItemTemplate>
   9:         <DataTemplate>
  10:             <Button Name="btnFilter" 
  11:                     Content="{Binding Name}"
  12:                     Click="btnFilter_Click" />
  13:         </DataTemplate>
  14:     </ItemsControl.ItemTemplate>
  15: </ItemsControl>

So the ItemsControl is bound to the Ratings list (here through the DefaultViewModel property for convenience, but you could just set icRatings’s ItemsSource to Ratings in code if you so prefer). That takes care of creating our filter buttons.

Next, what happens when a button is clicked ? Well, here’s the workhorse method :


   1: private async Task GetFilesAsync(StorageFolder rootFolder, string search = "")
   2: {
   3:     var options = new QueryOptions { FolderDepth = FolderDepth.Deep };
   4:     options.ApplicationSearchFilter = search;
   5:  
   6:  
   7:     var result = rootFolder.CreateFileQueryWithOptions(options);
   8:     var files = await result.GetFilesAsync();
   9:  
  10:     foreach (var file in files)
  11:     {
  12:         var myFile = new MyFile { FileName = file.Name };
  13:  
  14:         myFile.Thumbnail = await file.GetThumbnailAsync(ThumbnailMode.PicturesView, 200);
  15:  
  16:         var props = await file.Properties.RetrievePropertiesAsync(new List<string> { "System.RatingText", "System.Title" });
  17:         var title = props["System.Title"];
  18:         var ratingText = props["System.RatingText"];
  19:         myFile.Title = title != null ? title.ToString() : string.Empty;
  20:         myFile.RatingText = ratingText != null ? ratingText.ToString() : string.Empty;
  21:  
  22:         myFiles.Add(myFile);
  23:     }
  24: }

I start by creating a QueryOptions instance, and set its ApplicationSearchFilter to the filter criterion we’re interested in. In this case, search should be set in the button click handler :


   1: private async void btnFilter_Click(object sender, RoutedEventArgs e)
   2: {
   3:     myFiles.Clear();
   4:     
   5:     var rating = (sender as Button).DataContext as Rating;
   6:     if (rating != null)
   7:     {
   8:         ...
   9:         await GetFilesAsync(library, "System.Rating:" + ratingText);
  10:     }
  11: }

Note that I also set FolderDepth to Deep so as to specify a deep search. As we’ve seen before, I then create the query and start the async operation to retrieve the files matching the passed in search criterion. Then I loop through the result files, and seek to retrieve the actual rating value for the file (although I already know it since it should match the search criterion) as well as the image title. This is to illustrate accessing file properties using the new set of interfaces in WinRT Consumer Prev. Notice I first access the file’s Properties value and call RetrievePropertiesAsync() on that object, passing to it a list of property names I need to retrieve. The property names to pass are very badly documented at this point, but after some digging I found they must conform to Windows’ Advanced Query Syntax (documented here).  Here’s a list of Windows properties : http://msdn.microsoft.com/en-us/library/bb760685(VS.85).aspx

So anyway to make things short, the property we’re interested in is not System.Rating, which returns a number, but System.RatingText. So we retrieve that as well as Title, and we store the retrieved values in our MyFile data model object so we can bind to it in the UI. Here’s the updated version of MyFile :


   1: public class MyFile : BindableBase
   2: {
   3:     public string FileName { get; set; }
   4:  
   5:     string title;
   6:     public string Title 
   7:     {
   8:         get { return title; }
   9:         set { SetProperty<string>(ref title, value); }
  10:     }
  11:  
  12:     public StorageItemThumbnail Thumbnail { get; set; }
  13:     public string RatingText { get; set; }
  14: }

So hopefully, this has given you an idea of how you can implement searching/filtering on file data in your apps. I’ve used a simple example but you can obviously implement much more complex scenarios using these techniques.

Happy filtering…