PCL

Helper class to easily display local toast notifications (Windows Universal app)

Often , we need to display a confirmation that some action in our app has been finished (like some data has been updated etc.). There are several ways of doing this, like displaying a MessageBox or MessageDialog. This however breaks the user interaction, and a lot of users will start complaining on that if your app keeps doing so. There needs to be a better way.

With the Coding4fun Toolkit floating around, you can mimic a toast notification – sadly only on Windows Phone (at least for the moment, but Dave told me he will work on implementing it for Windows, too). Also, Toastinet library is floating around, which is also able to mimic the toast notification behavior (although for Windows Universal app, the implementation is not that intuitive as for Windows  Phone). Both are fantastic libraries that I used in the past, but I wanted a solution that is implemented easily and works with my Universal app. So I did some searching in the Web and the MSDN docs, and found out that is pretty easy to use the system toast notifications on both platforms locally.

There are 8 possible ways to format toast notifications (as you can see here in the toast template catalog). This gives us pretty much options on how a notification can be styled. However, most options just work on Windows 8.1, while Windows Phone 8.1 apps will only show the notification in the way “app logo”  “bold text”  “normal text”. However, the notification system takes care of that, so you can specify some other type on Windows 8.1, while knowing that it gets converted on Windows Phone automatically. This allows us to write a helper class that implements all possible options without any headache.

the code parts for the notification

Let’s have a look at the code parts for the notification. First, you need to add two Namespaces to the class:

using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;

After that, we can start writing our code. Toast notifications are formatted using Xml. Because of this, we need to get a reference to the underlying Xml template for the system toast notification:

ToastTemplateType xmlForToast= ToastTemplateType.ToastImageAndText01; 
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(xmlForToast);

System toast notifications can hold Text (and an Image on Windows 8.1). So we need to declare the elements of the toast notification. We are using the Xml methods of the DOM namespace to get the text elements of the chosen template first:

XmlNodeList toastTextElements = xmlForToast.GetElementsByTagName("text");
toastTextElements[0].AppendChild(xmlForToast.CreateTextNode("text1"));
//additional texts, depending on the template:
//toastTextElements[1].AppendChild(xmlForToast.CreateTextNode("text2"));
//toastTextElements[2].AppendChild(xmlForToast.CreateTextNode("text3"));

This is how the image element is implemented:

XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
//setting the image source uri:
if (toastImageElement != null) ((XmlElement) toastImageElement[0]).SetAttribute("src", imageSourceUri);
//setting optional alternative text for the image
if (toastImageElement != null)  ((XmlElement) toastImageElement[0]).SetAttribute("alt", imageSourceAlternativeText);

You can attach local or remote images to the toast notification, but remember this works only on Windows, not on Windows Phone.

The next part we are able to set is the duration. The duration options are long (25 seconds) and short (7 seconds). The default is short, which should be ok for most scenarios. Microsoft recommends to use long only when a personal interaction of the user is needed (like in a chat). This is how we do it:

IXmlNode toastRoot = xmlForToast.SelectSingleNode("/toast");
((XmlElement) toastRoot).SetAttribute("duration", "short");

What we are doing here is to get the root element of the template’s Xml and add a new element for the duration. Now that we finally have set all options, we are able to create our toast notification and display it to the user:

ToastNotification notification = new ToastNotification(xmlForToast);
ToastNotificationManager.CreateToastNotifier().Show(notification);

the helper class

That’s all we need to do for our local notification. You might see that always rewriting the same code just makes a lot of work. Because the code for the toast notification can be called nearly everywhere in an app (it does not matter if you are calling it from a ViewModel or code behind), I wrote this helper class that makes it even more easy to use the system toast notification locally:

    public class LocalToastHelper
    {
        public void ShowLocalToast(ToastTemplateType templateType, string toastText01, string toastText02 = null, string toastText03 = null, string imageSourceUri = null, string imageSourceAlternativeText = null, ToastDuration duration = ToastDuration.Short)
        {
            XmlDocument xmlForToast = ToastNotificationManager.GetTemplateContent(templateType);
            XmlNodeList toastTextElements = xmlForToast.GetElementsByTagName("text");

            switch (templateType)
            {
                case ToastTemplateType.ToastText01:
                case ToastTemplateType.ToastImageAndText01:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    break;
                case ToastTemplateType.ToastText02:
                case ToastTemplateType.ToastImageAndText02:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                        
                    }
                    ;
                    break;
                case ToastTemplateType.ToastText03:
                case ToastTemplateType.ToastImageAndText03:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                    }
                    ;
                    break;
                case ToastTemplateType.ToastText04:
                case ToastTemplateType.ToastImageAndText04:
                    toastTextElements[0].AppendChild(xmlForToast.CreateTextNode(toastText01));
                    if (toastText02 != null)
                    {
                        toastTextElements[1].AppendChild(xmlForToast.CreateTextNode(toastText02));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText02 must not be null when using this template type");
                    }
                    ;
                    if (toastText03 != null)
                    {
                        toastTextElements[2].AppendChild(xmlForToast.CreateTextNode(toastText03));
                    }
                    else
                    {
                        throw new ArgumentNullException("toastText03 must not be null when using this template type");
                    }
                    ;
                    break;
            }

            switch (templateType)
            {
                case ToastTemplateType.ToastImageAndText01:
                case ToastTemplateType.ToastImageAndText02:
                case ToastTemplateType.ToastImageAndText03:
                case ToastTemplateType.ToastImageAndText04:
                    if (!string.IsNullOrEmpty(imageSourceUri))
                    {
                        XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
                        if (toastImageElement != null)
                            ((XmlElement) toastImageElement[0]).SetAttribute("src", imageSourceUri);
                    }
                    else
                    {
                        throw new ArgumentNullException(
                            "imageSourceUri must not be null when using this template type");
                    }
                    if (!string.IsNullOrEmpty(imageSourceUri) && !string.IsNullOrEmpty(imageSourceAlternativeText))
                    {
                        XmlNodeList toastImageElement = xmlForToast.GetElementsByTagName("image");
                        if (toastImageElement != null)
                            ((XmlElement) toastImageElement[0]).SetAttribute("alt", imageSourceAlternativeText);
                    }
                    break;
                default:
                    break;
            }

            IXmlNode toastRoot = xmlForToast.SelectSingleNode("/toast");
            ((XmlElement) toastRoot).SetAttribute("duration", duration.ToString().ToLowerInvariant());

            ToastNotification notification = new ToastNotification(xmlForToast);
            ToastNotificationManager.CreateToastNotifier().Show(notification);
        }

        public enum ToastDuration
        {
            Short,
            Long
        }
    }

As you can see, you just need to provide the wanted parameters to the ShowLocalToast method, which will do the rest of the work for you.

One word to the second switch statement I am using. The image element needs to be set only when we are using the ToastImageAndTextXX templates. There are three ways to implement the integration: using an if with 4  “or” options, the switch statement I am using or a string comparison with String.Contains. The switch statement is the cleanest option for me, so I decided to go this way. Feel free to use any of the other ways in your implementation.

In my implementation, I added also some possible ArgumentNullExceptions to make it easy to find any usage errors.

For your convenience, I attached the source file. Just swap out the namespace with yours. Download

The usage of the class is pretty simple:

var _toastHelper = new LocalToastHelper();
_toastHelper.ShowLocalToast(ToastTemplateType.ToastText02, "This is text 1", "This is text 2");

audio options

The system toasts have another option that can be set: the toast audio. This way, you can customize the appearance of the toast a bit more. I did not implement it yet, because there are some more options and things to remind, and I haven’t checked them out all together. Once I did, I will add a second post to this one with the new information.

As always, I hope this is helpful for some of you.

Happy coding!

Posted by msicc in Archive, 3 comments

How to update a live tile in a background task with web data on Windows 8.1

scheduledtaskWindows

Like I wrote in my last post, I recently finished a PCL project with a Windows Phone and a Windows 8.1 app. Like the Windows Phone version, also the Windows 8.1 app has a live tile that fetches the same data from WordPress.

Taking advantage of our PCL solution structure, we are able to reuse the JSON data class from our PCL.

However, there are a few things that differ from the Windows Phone background agent.

First, we need to add a new project to our solution. In the new project dialog, select ‘Windows Runtime Component’ in the C# Windows Store section.

To make it a background task, implement the interface IBackgroundTask to the generated class in our Runtime component. In order to make it doing some work, we are going to add the ‘Run’-method that will start our background agent.

Here we are already with the first difference. The Windows Runtime does not support direct async Tasks of type string (I fetch all articles via HttpClient in an async task that returns the JSON string). That’s why we need to wrap it in an IAsyncOperation. To do this, add a new sealed class to your Windows Runtime background agent project.

Here is how the code for the IAyncOperation:

        public IAsyncOperation<string> GetLastPostJsonString(string url)
        {
            try
            {
                return AsyncInfo.Run((System.Threading.CancellationToken ct) => GetInternal(url));
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
            return null;
        }

        private static async Task<string> GetInternal(string url)
        {
            try
            {
                HttpClient getJsonStringClient = new HttpClient();
                getJsonStringClient.DefaultRequestHeaders.IfModifiedSince = DateTime.Now;

                var response = await getJsonStringClient.GetAsync(url);
                var JsonString = await response.Content.ReadAsStringAsync();
                return JsonString;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
            return null;
        }

Let me explain the code. The IAsyncOperation starts and returns the string of our async Task<string>.  To achieve this, we need to start another async Run that delegates our Task<string> result to the main background task. Unlike a direct HttpClient call, we are not able to save the string directly to an string object. Instead, we need to read the stream.

I highly recommend to wrap this all in try/catch blocks to prevent any crashes of your app.

Let’s go back to our main background task class. Now we are able to fetch the JSON string from web service, we finally can start to implement the Run() method that will actually update our live tiles:

       public async void Run(IBackgroundTaskInstance taskInstance)
       {
           try
           {
               BackgroundTaskDeferral deferral = taskInstance.GetDeferral();

               PostfetcherForTileUpdate fetchJsonString = new PostfetcherForTileUpdate();

               Constants.latestPostsForLiveTileFromWordPressDotComResultString = await fetchJsonString.GetLastPostJsonString(Constants.GetLatestPostsForLiveTileFromWordPressDotComUriString);

               );

               UpdateTile();

               deferral.Complete();
           }
           catch (Exception ex)
           {
               Debug.WriteLine(ex.ToString());
           }
       }

The Run method needs to implement the Interface for IBackgroundTaskInstance. To inform the OS that our background agent may do work after the Run has returned data, we need to use the GetDeferral() method. If we would do that, it may happen that the task will be closed before all action has taken place.

After getting the JSON String from the IAsyncOperation, we can now write it to an object, call our UpdateTile() method and tell the system that our deferral is completed.

Within the UpdateTile() method, we are going to deserialize the JSON String and update our live tile. There are a lot of considerations for the live tiles in Windows 8 and 8.1, make sure you have read the guidelines for tiles and badges on MSDN. Make also sure you check the tile template catalogue to choose the right templates for your app.

Let’s have a look at the UpdateTile() code:

        private static void UpdateTile()
        {
            try
            {
                //create a new Tile updater and allow it to be added to the notification queue
                var updater = TileUpdateManager.CreateTileUpdaterForApplication();
                updater.EnableNotificationQueue(true);
                updater.Clear();

        //deserialize the JSON string
                var LatestPostFromWordpress = JsonConvert.DeserializeObject<json_data_class_Posts.Posts>(Constants.latestPostsForLiveTileFromWordPressDotComResultString);

        //fill in the Tile Templates
                foreach (var item in LatestPostFromWordpress.posts)
                {
            //supporting both Medium and Wide tiles
                    XmlDocument WidetileXML = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150PeekImage04);
                    XmlDocument SquareTileXML = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare150x150PeekImageAndText04);

            //setting the text on our tiles
                    var title = item.title;
                    WidetileXML.GetElementsByTagName("text")[0].InnerText = title;
                    SquareTileXML.GetElementsByTagName("text")[0].InnerText = title;

            //providing the remote image source 
            //if not empty:
                    if (item.featured_image != string.Empty)
                    {
                        XmlNodeList tileImageAttributes = SquareTileXML.GetElementsByTagName("image");
                        ((XmlElement)tileImageAttributes[0]).SetAttribute("src", item.featured_image);
                        ((XmlElement)tileImageAttributes[0]).SetAttribute("alt", "no image");

                        XmlNodeList tileWideImageAttributes = WidetileXML.GetElementsByTagName("image");
                        ((XmlElement)tileWideImageAttributes[0]).SetAttribute("src", item.featured_image);
                        ((XmlElement)tileWideImageAttributes[0]).SetAttribute("alt", "no image");
                    }
            //if empty:
                    else if (item.featured_image == string.Empty)
                    {
                        XmlNodeList tileImageAttributes = WidetileXML.GetElementsByTagName("image");
            //this image has to be in the main Windows 8.1 project, not in the background task!
                        ((XmlElement)tileImageAttributes[0]).SetAttribute("src", "ms-appx:///Images/NoImgPlaceholderMedium.png");
                        ((XmlElement)tileImageAttributes[0]).SetAttribute("alt", "no image");

                        XmlNodeList tileWideImageAttributes = WidetileXML.GetElementsByTagName("image");
            //this image has to be in the main Windows 8.1 project, not in the background task!
                        ((XmlElement)tileWideImageAttributes[0]).SetAttribute("src", "ms-appx:///Images/NoImgPlaceholderWide.png");
                        ((XmlElement)tileWideImageAttributes[0]).SetAttribute("alt", "no image");
                    }

            //perform the update of our live tiles
                    updater.Update(new TileNotification(WidetileXML));
                    updater.Update(new TileNotification(SquareTileXML));
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
        }

To make the background task updating our tile, we need to use the TileUpdateManager class. As we are updating our main tile, we are using the CreateTileUpdaterForApplication() method. To make sure our tile is able to queue our update request, we are setting the EnableNotificationQueue property to true.

After deserializing the JSON string, we fill the Template by using DOM methods. I used the GetElementsByTagName(string) method to find the title and the image ChildNodes in the template. This way, I don’t need to care for the Xml structure.

The title content can be set by using the InnerText property. For providing the images source, we need to go through the NodeList, searching for the image NodeChild and use the SetAttribute() method of the XmlElement class.

With the code above, the tile gets a placeholder image, if the JSON string does not return an url for the  featured image. Important: the placeholder image has to be in your main Windows 8 project, not in the background task project. Make also sure that the image(s) have a unique name and do not use the same file name like your base tile image.

That’s all we need to do in our background task project.

Let’s have a look at our main app project. First, we have to declare our background task to make our app registering for the periodic notifications. Open the Package.appxmanifest of your app and navigate to the ‘Declarations’ tab.

Screenshot (259)

Under ‘Available Declarations’, select ‘Background Tasks’ and ‘Add’.

Screenshot (259)

 

Under ‘Description’ check the ‘Timer’ Option. Make sure you referenced your background task project and add it as ‘Entry Point’.

That’s all that wee need to set up in the first step. Now we are going to write our method to register the background task:

        //needs to be async because of the BackgroundExecutionManager
        public async void RegisterBackgroundTask()
        {
            try
            {
        // calling the BackgroundExecutionManager
        //this performs the message prompt to the user that allows the update of our tiles and the permissions entry
                var backgroundAccessStatus = await BackgroundExecutionManager.RequestAccessAsync();

        //checking if we have access to set up our live tile
        if (backgroundAccessStatus == BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity ||
                    backgroundAccessStatus == BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity)
                {
            //unregistering our old task, if there is one                   
            foreach (var task in BackgroundTaskRegistration.AllTasks)
                    {
                        if (task.Value.Name == taskName)
                        {
                            task.Value.Unregister(true);
                        }
                    }

            //building up our new task and registering it
                    BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder();
                    taskBuilder.Name = taskName;
                    taskBuilder.TaskEntryPoint = taskEntryPoint;
                    taskBuilder.SetTrigger(new TimeTrigger(60, false));
                    var registration = taskBuilder.Register();
                }
            }
        //catching all exceptions that can happen
            catch (Exception ex)
            {
        //async method used, but wil be marked by VS to be executed synchronously
                var message = new MessageDialog("we were not able to activate the live tile. Please restart the application to try again.");
                message.Title = "Sorry,";
                message.ShowAsync();

            }

        }

We are calling the BackgroundExecutionManager of Windows 8.1. This adds the Permissions dialog on the app’s first run as well as the entry in the settings charm. Only if we are allowed by the user, our tile will be updated.

Unregistering already running tasks and re-adding them is a common practice, which I did also here.

After that, I build up a new background task with the BackgroundTaskBuilder class, referencing to the taskEntryPoint I set up in the Declarations tab before.

Unlike the Windows Phone background agent, we can change the time of the background task. I used 60 minutes for this app. You will need to specify a trigger (minimum of 15 minutes). I tried it without, which lead to an exception.

I am catching all exceptions and display a message to the user here. The method will be marked as executing synchronously, but it will work. The reason is that no async methods are allowed in the catch block.

Like always, I added this to my App.xaml.cs file, as this is set up application wide. To start the task, I just call it in the Application.Launched event. It would also work in the OnWindowCreated event, if you want your Launched event free.

Setting up a background task to update your live tile with data from web is not as easy as on Windows Phone, but with this article you will be able to get started on that.

As always, I hope that this post is helpful for some of you.

Happy coding, everyone!

Posted by msicc in Archive, 1 comment

How to update a live tile in a background agent with web data on Windows Phone

scheduledtaskWP

In one of my recent projects, I needed to update the live tile of the app via a background agent with data from a WordPress based web site. I did what I always do when implementing a feature I never used before: researching.

I found tons of example on how to update the live tile periodically, but non of them told me how I can use the data from the web site or where I need to put it in.

In the end, it was Jay Bennet, developer of the fantatsic WPcentral Windows Phone app, who gave me the last hint I needed – where do I start the request for the data I need. Thanks to him for that!

Ok, but let’s start in the beginning. When it comes to web based services, you first need a class (or ViewModel) that can hold your data you receive from your service. I explained that already pretty well here. No matter if you are running your request within your app project or in a PCL, it pretty much always works like this.

After stripping of our class out of the JSON string, we are now able to create our request as well as our Background Agent.

The first thing you need to do is to add a Windows Phone Scheduled Task Agent. Create a new project within your app and choose the project type mentioned before. Then, in your main project, add it as a reference (right click on References/Add Reference/Solution => select your background agent project there. That’s it.

No go to your ‘ScheduledAgent.cs’ file and open it. You will find this code in there:

protected override void OnInvoke(ScheduledTask task)
{
  //TODO: Add code to perform your task in background

  NotifyComplete();
}

And thanks to Jay, I know that this is where all the action (not only updating the tile like in all samples I found) happens. This may sound very trivial for those of you who have experience with that, but if you’re new to it, it may hold you back a bit. However, there are a few points you’ll need to take care of:

    • you only have 25 seconds to perform all action here
    • your task will run every 30 minutes, no way to change that
    • scheduled tasks need to be restarted within 14 days after the current start
    • Battery saver and the OS/the user can deactivate the agent
    • there is a long list of what you are not able to do in here (check MSDN)
    • there are memory limits (check MSDN)

all action needs to be finished before NotifyComplete() is called

Based on this information, I created my task as following:

        protected async override void OnInvoke(ScheduledTask task)
        {
        //performing an async request to get the JSON data string        
            PostsFetcher postfetcher = new PostsFetcher();
            Constants.latestPostsForLiveTileFromWordPressDotComResultString = await postfetcher.GetLatestPostFromWordPressDotComForLiveTileOnPhone();

        //deserialize all data
            var lifetileBaseData = JsonConvert.DeserializeObject<json_data_class_Posts.Posts>(Constants.latestPostsForLiveTileFromWordPressDotComResultString);

            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
        //using Telerik's LiveTileHelper here
                Uri launchUri = new Uri("Mainpage.xaml", UriKind.Relative);
                RadFlipTileData fliptileData = new RadFlipTileData();
                fliptileData.BackContent = lifetileBaseData.posts[0].title;
                fliptileData.WideBackContent = lifetileBaseData.posts[0].title;
                fliptileData.BackTitle = string.Format("{0} {1}", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString());
                fliptileData.Title = "LiveTileTitle";               

                if (lifetileBaseData.posts[0].featured_image != string.Empty)
                {
                    fliptileData.BackgroundImage = new Uri(lifetileBaseData.posts[0].featured_image, UriKind.RelativeOrAbsolute);
                    fliptileData.WideBackgroundImage = new Uri(lifetileBaseData.posts[0].featured_image, UriKind.RelativeOrAbsolute);
                    fliptileData.BackBackgroundImage = new Uri("Images/BackBackground.png", UriKind.RelativeOrAbsolute);
                    fliptileData.WideBackBackgroundImage = new Uri("Images/WideBackBackground.png", UriKind.RelativeOrAbsolute);
                }
                else
                {
                    fliptileData.BackgroundImage = new Uri("Images/PlaceholderBackgroundImage.png", UriKind.RelativeOrAbsolute);
                    fliptileData.WideBackgroundImage = new Uri("Images/PlaceholderWideBackgroundImage.png", UriKind.RelativeOrAbsolute);
                    fliptileData.BackBackgroundImage = new Uri("Images/BackBackground.png", UriKind.RelativeOrAbsolute);
                    fliptileData.WideBackBackgroundImage = new Uri("Images/WideBackBackground.png", UriKind.RelativeOrAbsolute);
                }

                foreach (ShellTile tile in ShellTile.ActiveTiles)
                {
                    LiveTileHelper.UpdateTile(tile, fliptileData);
                }
            });

            NotifyComplete();
        }

As I fetch different data from the site, I create a class that holds all request methods. In those methods, I just created an HttpClient that downloads the desired Json string into my app. I take only the first post in the case above, to make the live tile updating also on slow internet connections within the 25 seconds and to not reach any memory limit. In the end, I use Telerik’s LiveTileHelper to create a FlipTile with image and text from the site (the image will be downloaded automatically).

That’s all we need to to in the Scheduled Task Agent. Now we need to implement the agent into our app project.

First thing we should implement is a switch where the user can turn our agent on and off. I used a ToggleSwitch for that, saving the isChecked state as a Boolean to the IsolatedStorage of my app.

Knowing I need to restart the background agent after some time, I implemented the agent handling in App.xaml.cs. This way, I need to write less code as I tend to use separate settings pages. The only thing you need to think of is to set up all objects as static and public.

First, we need to declare a PeriodicTask as well as a name for it:

public static PeriodicTask LiveTileUpdaterPeriodicTask; 
public static string LiveTileUpdaterPeriodicTaskNameString = "LiveTileUpdaterPeriodicTaskAgent";

Now we need to generate a method that handles everything for our background task:

       public static void StartLiveTileUpdaterPeriodicTaskAgent()
        {
        //declare the task and find the already running agent
            LiveTileUpdaterPeriodicTask = ScheduledActionService.Find(LiveTileUpdaterPeriodicTaskNameString) as PeriodicTask;

            if (LiveTileUpdaterPeriodicTask != null)
            {
        //separate method, because we need to stop the agent when the user switches the Toggle to 'Off'
                StopLiveTileUpdaterPeriodicTaskAgent();
        //contains:
        //try
                //{
                //  ScheduledActionService.Remove(App.LiveTileUpdaterPeriodicTaskNameString);
                //}
                //catch { }
            }

        //generate a new background task 
            LiveTileUpdaterPeriodicTask = new PeriodicTask(LiveTileUpdaterPeriodicTaskNameString);

        //provide a description. if not, your agent and your app may crash without even noticing you while debugging
            LiveTileUpdaterPeriodicTask.Description = "This background agent checks every 30 minutes if there is a new blog post.";

        //start the agent and error handling
            try
            {
                ScheduledActionService.Add(LiveTileUpdaterPeriodicTask);
            }
        catch (InvalidOperationException exception)
            {
        //user deactivated or blocked the agent in phone settings/background tasks. Ask him to re-activate or unblock it
                if (exception.Message.Contains("BNS Error: The action is disabled"))
                {
                    RadMessageBox.ShowAsync("it seems you deactivated our Background Agent for the Live Tiles. Please go to settings/background tasks to activate our app again.", "Whoops!", MessageBoxButtons.OK);
                }

        //the maximum of running background agents is reached. No further notification to the user required, as this is handled by the OS
                if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
                {
                    //changing the Boolean to false, because the OS does not allow any new taks
                    isLiveTileActivated = false;
                }
            }
            catch (SchedulerServiceException)
            {
                //if there is a problem with the service, changing the Boolean to false. 
        //feel free to inform the user about the exception and provide additional info
                isLiveTileActivated = false;
            }
        }

In Application.Launching() and in ToggleSwitch_Checked event, call this method. In ToggleSwitch_UnChecked event, call ‘StopLiveTileUpdaterPeriodicTaskAgent()’ instead to stop the agent.

Before we are now able to debug our agent, there are two things left. The first thing is to declare

#define DEBUG_AGENT

in the very first line of ‘App.xaml.cs’ as well as in ‘ScheduledAgent.cs’ before all using statements.

The second thing is adding an if-Debug command to our ‘StartLiveTileUpdaterPeriodicTaskAgent()’ and also to our ‘OnInvoke(ScheduledTask task)’ methods:

#if(DEBUG_AGENT)
 ScheduledActionService.LaunchForTest(LiveTileUpdaterPeriodicTaskNameString, TimeSpan.FromSeconds(60));
 #endif

Add this after adding the task to the SchedulerService in our agent starter and before ‘NotifyComplete()’ in ‘ScheduledAgent.cs’.

If you run the project now in Debug mode, your agent will be debugged and forced to run after 60 seconds. If you run the project in Release mode, Visual Studio will throw an ‘Access Denied’ error, so make sure you set it up correctly.

If you follow all steps above, you will be able to add a Live Tile updated via background agent very easily to your app.

As always, I hope this real world scenario will help some of you.

Until then, happy coding!

Posted by msicc in Archive, 7 comments