Windows Azure Mobile Services is a awesome product that will help you to deliver great apps using a Windows Azure backend.

One of the feature I like is the possibility to send push notifications to modify tiles in just few lines of code. There is just a small drawback to this technique: as the queue notification must be enable from the client code, you won’t be able to get animated tiles.

But (as there is always a “but” ;), the next part of this post will provide you a simple but useful technique to get animated tiles for you Windows Store app.

In fact, instead of of send a classic notification, we’ll use the sendRaw method of the wns object to send a Raw notification. As a reminder, a raw notification is a notification that’s not displayed on the screen: as a developer, your code is notified that the notification is coming and you have to perform what you want/need.

So first, you have to create your server script (here is mine as an example):

var LASTEST_QUOTES_NUMBER = 5;

var payload;
var idx;

function cleanChannels(callback) {
    var channelsTable  = tables.getTable('Channel');

    channelsTable.read({
        success: function(channels) {
            if(channels.length > 0) {
                channels.forEach(function(channel){
                    if(channel.expirationTime < new Date()){
                        channelsTable.del(channel.id);
                    }
                })
            }

            if(callback) {
                callback();
            }
        }
    })
}

function preparePayload(movie, quote){
     payload += "<QuoteWithMovie>" +
                    "<Movie Title=\"" + movie.title + "\" FrontCover=\"" + movie.poster_path + "\" BackCover=\"" + movie.backdrop_path + "\" />" +
                    "<Quote Text=\"" + quote.text.substring(100) + "\" />" +
                "</QuoteWithMovie>";

    idx++;

    if(idx == LASTEST_QUOTES_NUMBER) {

        payload += "</Results>";

        cleanChannels(function() {
            var channelsTable  = tables.getTable('Channel');

            channelsTable.read({
                success: function(channels) {
                    if(channels.length > 0) {
                        for (var i = 0; i < channels.length; i++) {
                            var channel = channels[i];

                            push.wns.sendRaw(channel.uri, payload);
                        }
                    }
                }
            });
        });
    }
}

function sendLatestQuotes() {

    payload = "";
    idx = 0;

    var moviesTable = tables.getTable('Movie');
    var quotesTable = tables.getTable('Quote');

    quotesTable.where({ ispublished: true })
    .orderBy('date')
    .take(LASTEST_QUOTES_NUMBER)
    .read({
        success: function(latestQuotes) {
            payload += "<Results>";

            latestQuotes.forEach(function(quote) {
                moviesTable.where({ movieid: quote.movieid })
                .read({
                    success: function(movies) {
                       preparePayload(movies[0], quote);
                    }
                })
            })
        }
    })
}

As you can see, the code is pretty simple: a XML payload is construct from some database records and, when I have the number of notification I want (5 because notification queuing allow you to switch up to 5 notifications), I send a raw notification, using the payload constructed before.

On the client side, the code use the PushNotificationReceived event to be notified and, if a raw notification is coming, the code use the content (the XML payload) to update the tile:

 var currentChannel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
currentChannel.PushNotificationReceived += async (sender, args) =>
{
    if (args.NotificationType == PushNotificationType.Raw && args.RawNotification != null)
    {
        await MainTileUpdateTask.UpdateMainTileAsync(args.RawNotification.Content);

        // The raw notification should not be passed to its background task
        args.Cancel = true;
    }
};

The code to update the tile and animate it is dedicated to your application but, for you records, here is the one I’m using:

internal static async Task UpdateMainTile(string content)
{
    _isUpdatingTile = true;

    var tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
    tileUpdater.EnableNotificationQueue(true);

    tileUpdater.Clear();

    var xElement = XElement.Parse(content);
    var quotesWithMovies = from q in xElement.Elements("QuoteWithMovie")
                                            let movie = q.Element("Movie")
                                            let quote = q.Element("Quote")
                                            select new QuoteWithMovie
                                            {
                                                  MovieTitle = movie.Attribute("Title").Value,
                                                  FrontCover = movie.Attribute("FrontCover").Value,
                                                  BackCover = movie.Attribute("BackCover").Value,
                                                  Text = quote.Attribute("Text").Value
                                            };

    var quoteWithMovies = quotesWithMovies as IList<QuoteWithMovie> ?? quotesWithMovies.ToList();
    if (quoteWithMovies.Any())
    {
        for (int index = 0; index < quoteWithMovies.Count; index++)
        {
            var quote = quoteWithMovies[index];

            var frontCoverFileFullUrl =
            await MoviesService.Current.GetFullCoverImageUrlAsync(quote.FrontCover, "w154");
            var backCoverFileFullUrl =
            await MoviesService.Current.GetFullCoverImageUrlAsync(quote.BackCover, "w300");

            var squareImageFilename = string.Format("Square_{0}.jpg", index);
            var wideImageFilename = string.Format("Wide_{0}.jpg", index);

            FileHelpers.DownloadFile(ApplicationData.Current.LocalFolder, squareImageFilename,
            frontCoverFileFullUrl);

            FileHelpers.DownloadFile(ApplicationData.Current.LocalFolder, wideImageFilename,
            backCoverFileFullUrl);

            var wideTile = Core.UI.Notifications.TileContentFactory.CreateTileWideImageAndText01();
            wideTile.Branding = TileBranding.None;
            wideTile.RequireSquareContent = false;
            wideTile.Image.Src = string.Format("ms-appdata:///local/{0}", wideImageFilename);
            wideTile.Image.Alt = quote.MovieTitle;
            wideTile.TextCaptionWrap.Text = quote.Text;

            var squareTile = Core.UI.Notifications.TileContentFactory.CreateTileSquareImage();
            squareTile.Branding = TileBranding.None;
            squareTile.Image.Src = string.Format("ms-appdata:///local/{0}", squareImageFilename);
            squareTile.Image.Alt = quote.MovieTitle;

            var wideTileNotification = wideTile.CreateNotification();
            var squareTileNotification = squareTile.CreateNotification();

             tileUpdater.Update(wideTileNotification);
             tileUpdater.Update(squareTileNotification);
         }
    }

    _isUpdatingTile = false;
}

The might be others techniques to perform the same results but I like this one for one reason: raw notification can be received by BackgroundTask!

So if you add a BackgroundTask (which set its trigger to PushNotificationTrigger) and put you server code in a Mobile Services Scheduler, you’ll be able to update the tile even if the app is not running!

 

Happy coding!

During my last holidays, I’ve started to work on a new Windows 8 project (more details on that later) that use Windows Azure Mobile Services as a backend.

During the development, I’ve encountered the following issue which, I suppose, is a bug that will be fixed later.

On my application, I used the following code:

var allItems = await ServiceClient.GetTable<Items>().Where(i => i.IsPublished).ToListAsync();

return allItems;

Unfortunately, this code thrown, every time, the following error:

image

After taking a look in Fiddler, I found the problem:

image

As you may know, Mobile Services use REST to access data on the server and the following REST query does not seem to be valid because there is no query operator (like eq for equal, etc.) and no value. So, I’ve tried to modify my code like this:

var allItems = await ServiceClient.GetTable<Items>().Where(i => i.IsPublished == true).ToListAsync();

return allItems;

As you can see, I’ve just append “== true” to the Where clause and all seems to work like a charm. Indeed, no exception was rise and, looking at the REST query, we can see that all is OK:

image

 

Happy coding!