Saturday, 27 September 2014

Background Task

In a winRT app it's all or nothing, that is to say that if you app isn't visible it's not running, No background polling, no heartbeat, nothing for all intensive purposes you can assume your app is in a suspended state in which nothing is happening. However, there is hope in something called a background task, now this background task runs in a restricted environment with limited resources, but beggars can't be choosers.

Needless to say but they should really only be used to fire small bits of code to inform your user of something and not used for any heavy lifting.

To get started you're going to have to add a WinMD project to your solution.

with that done add a reference from your Application Project to the WinMD project.

in the above the Application project is pc.Background and the WinMD project is pc.BackgorundTask, with your reference made open up the only class in your WinMD Project, it should look pretty bare bones.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace pc.BackgroundTask
{
    public sealed class Class1
    {
    }
}


Now let's change it from Class1 to something a little more descriptive, where going to fire background task when the application is added to the lock screen. so lets rename Class1 to AppendedToLockScreenBackgroundTask, with that change we are also going to have to implement the IBackgroundTask interface, it only implements one method and it's Run, this is the method that will asynchronously fire. I mention Asynchronously because we're going to have to grab a deferral to keep our Background task from falling out of scope before we fire our code.

with our changes complete we should have something along the lines of

using Windows.ApplicationModel.Background;

namespace pc.BackgroundTask
{
    public sealed class AppendedToLockScreenBackgroundTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            var deferral = taskInstance.GetDeferral();

            //Our Logic goes here

            deferral.Complete();
        }
    }
}


Now what to do, for now lets do something very simple and just add a toast notification, refer to my previous post on Toast's to add the logic.

With our Background Task, written we now need to go back to our application project and add some declarations into our manifest, so open up the "Package.appxmanifest" file and go to the declarations tab. Once there add the Background Tasks declaration specify the System Event and set the Entry Point to the Namespace.Class that implements your Run method

Now that's done, go back to the Application Tab and Set the Lock screen notification to Badge
and set the Toast Capable to Yes

Next you'll have to add the visual assets for badge notifications, i just made min in MS Paint,

Now with all that configuration out of the way let's register our background Task, open up your MainPage.xaml View and in the constructor register the event

//check to make sure that your background task isn't already registred
if (BackgroundTaskRegistration.AllTasks.FirstOrDefault(t => t.Value.Name == "AppendToLockScreen").Value == null)
{
    var builder = new BackgroundTaskBuilder();
    builder.Name = "AppendToLockScreen";
    builder.TaskEntryPoint = "pc.BackgroundTask.AppendedToLockScreenBackgroundTask";
    builder.SetTrigger(new SystemTrigger(SystemTriggerType.LockScreenApplicationAdded, false));

    var taskRegistration = builder.Register();

}

The above code registers the background event, now whenever the user adds the application to the Lock screen, it'll fire our toast notification.


Saturday, 20 September 2014

Background TimerTask 01

In my previous post we made a background timer task, a rather contrived example, but it got the idea across. Now let's continue with it but add some bells and whistles, let's say that we want to fetch some data from a web service, one thing we can guarantee is that for our background task to work we'll need an internet connection, otherwise it's senseless to fire our task, luckily we can easily add a condition to our task when we register it requiring an internet connection.

//Add internet condition
builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));

now that's nice, but once we fire our background task to fetch some data we probably want to do something with that data in our application, luckily we can also create a callback function to handle it, so let's do that, One important thing to note is that you can't create a callback function for an indefinite timer, that means for this to work you have to set the OneShot parameter to true otherwise your call back will never fire and you wont know why.

Change the Task trigger to be One Shot

//waits 15 minutes to schedual the background task, once scedualed it
//will fire within 15 minutes once
builder.SetTrigger(new TimeTrigger(15, true));

Add a callback to our task

taskRegistration.Completed += taskRegistration_Completed;

next create the callback, on the view I created a Text box  named Output so that I could display my results to the user. Now since there really is no way to directly pass a result back to your application from your background task i utilize the Application data store to do this.

private async void taskRegistration_Completed(BackgroundTaskRegistration sender,
    BackgroundTaskCompletedEventArgs args)
{
    await CoreApplication.MainView.CoreWindow.Dispatcher
                .RunAsync(CoreDispatcherPriority.Normal,
                        () => Output.Text = "Task Fired");

    if (sender.Name == "TimerTask")
        try
        {
            args.CheckResult();

            var localSettings = ApplicationData.Current.LocalSettings;
            var data = localSettings.Values["data"].ToString();

            await CoreApplication.MainView.CoreWindow.Dispatcher
                .RunAsync(CoreDispatcherPriority.Normal,
                            () => Output.Text = data);
        }
        catch (Exception ex)
        {
            var data = String.Format("{0} Error:{1}", Output.Text, ex.Message);

            CoreApplication.MainView.CoreWindow.Dispatcher
                .RunAsync(CoreDispatcherPriority.Normal,
                        () => Output.Text = data).AsTask().RunSynchronously();

        }

}

notice that we check our background task for errors, you never know when your connecting to external resources something could always go wrong and you should always check it. With our callback function complete lets modify our background task to pass back the current date time

using System;
using Windows.ApplicationModel.Background;
using Windows.UI.Notifications;

namespace pc.BackgroundTimerTask
{
    public sealed class TimerTask :IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            var d = taskInstance.GetDeferral();
            this.SendToast("Data Refreshed");
            string[] data = new string[1];
            
            //Deliberate Error
            //data[1] = DateTime.Now.ToString();
           
            var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;

            localSettings.Values["data"] = DateTime.Now.ToString();
           
            d.Complete();
        }

        private void SendToast(string Message)
        {
            var ToastXml = ToastNotificationManager
                .GetTemplateContent(ToastTemplateType.ToastText01);

            //extract all the text elements
            var toastTextElements = ToastXml.GetElementsByTagName("text");

            //set the one text element we have to a value
            toastTextElements[0].AppendChild(ToastXml.CreateTextNode(Message));

            //create the toast
            var Toast = new ToastNotification(ToastXml);

            //show the toast in the top right corner
            ToastNotificationManager.CreateToastNotifier().Show(Toast);

        }
    }
}

as you may notice I refactored our Background task a little bit, and pass my value back to my application using the Application data store. now I also, tried createing an exception within my application to try and handle it on the completed callback, but I haven't been able to figure this out, it seems that the background task throws my exception and the callback never fires.

now one thing to note is that our callback will only fire while our app is running, however a background task will fire whether the app is running, suspended or even terminated. so keep in mind that our TaskCompleted event will then fire next time we activate our application.

pretty cool eh?




Monday, 8 September 2014

Background TimerTask 00

Let's create a timed background task:

Create a regular winRT store app project, I'm going to name mine pc.BackgroundTimer

Once that's done add a winMD (windows run-time component) project to the same solution, I named id pc.BackgroundTimerTask

with that done create a reference from your winRT application to your winMD component. 

with that complete, rename the class1.cs file in your winMD component to something more relevant, I went with TimerTask because I'm super creative... 

Once your class1.cs is renamed open it up and implement the IBackgroundTask interface. That should give your the run method, this method will be what does the work in your background task.

using Windows.ApplicationModel.Background;
using Windows.UI.Notifications;

namespace pc.BackgroundTimerTask
{
    public sealed class TimerTask :IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
        
        }
    }
}

lets add some logic in here, I go with a toast notification 

using Windows.ApplicationModel.Background;
using Windows.UI.Notifications;

namespace pc.BackgroundTimerTask
{
    public sealed class TimerTask :IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            var d = taskInstance.GetDeferral();

            var ToastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);

            //extract all the text elements
            var toastTextElements = ToastXml.GetElementsByTagName("text");

            //set the one text element we have to a value
            toastTextElements[0].AppendChild(ToastXml.CreateTextNode("Woot Woot!!!"));

            //create the toast
            var Toast = new ToastNotification(ToastXml);

            //show the toast in the top right corner
            ToastNotificationManager.CreateToastNotifier().Show(Toast);

            d.Complete();
        }
    }
}


I like to go with a toast notification to make displaying to the user simpler, but you could do a multitude of things here.

so with that done make note of the namespace.className so in my case it's "pc.BackgroundTimerTask.TimerTask", copy that into your clipboard. Now open your the app manifest of your winRT application and go to the Declarations Tab, here you'll have to add a background task, specify that it's a timer, and add the Entry point, which is the "namespace.ClassName" of your task that i told you to note earlier.


with that done, go to the Application tab

here you're going to have to set your toast Capable to yes, why? Because our background task makes toasts. Also set the lock screen notification to badge, why? i'm not really sure, but it's going to force you to make visual assets for your badge as well it will require your app to be pinned to the lock screen for your timer job to fire in windows 8.1 apparently this restriction has been removed for windows 10.

Now go to the visual assets page and follow the errors

You should see the above, just add icons with the corresponding sizes, you only have to add one, Usually the largest, but i generally add all three.

With all that done you can build your solution, it still does nothing, but at least it builds. So lets lets register our background task

using System.Linq;
using Windows.ApplicationModel.Background;
using Windows.UI.Xaml.Controls;

namespace pc.BackgroundTimer
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            //check to make sure that your background task isn't already registred
            if (BackgroundTaskRegistration
                    .AllTasks
                    .FirstOrDefault(t => t.Value.Name == "TimerTask").Value == null)
            {
                var builder = new BackgroundTaskBuilder();
                builder.Name = "TimerTask";
                builder.TaskEntryPoint = "pc.BackgroundTimerTask.TimerTask";
                //waits 15 minutes to schedual the background task, once scheduled it
                //will fire within 15 minutes, indefinitely 
                builder.SetTrigger(new TimeTrigger(15, false));

                var taskRegistration = builder.Register();
            }
        }
    }
}

To keep from registering our task every time we start our app we first check to make sure it's not already registered, the we set it to be scheduled after 15 minutes, once it's scheduled it will fire within 15 minutes. The freshness timer that is the 15 minutes before being scheduled is a variable with the lowest value accepted being 15 minuets anything below that and you'll get an error, the second 15 that is the fired within is a constant, so whether you set your freshness timer to the minimum 15 minutes or 30 minutes (not sure if there's an upper limit) it will still fire within 15 minutes of being scheduled.

now waiting 15 minutes to 30 minutes to test your app seems silly, luckily to trouble shoot it you can force the background task to fire early. Use the  debug toolbar to force the Background task to fire.


and there you go you have a timer task firing.




Monday, 1 September 2014

Toast Notification

Toast Notifications are those little rectangles in the top right corner that inform the user of something, whether it's that you've got mail or simply a replacement for the dreaded successful feedback message box. So let's create a simple Toast Notification add a button named ToastButton to your form and give it a click event handler

void ToastButton_Click(object sender, RoutedEventArgs e)
{
    //Initialize the toast xml, using a predefiend type.
    var ToastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);

    //extract all the text elements
    var toastTextElements = ToastXml.GetElementsByTagName("text");

    //set the one text element we have to a value
    toastTextElements[0].AppendChild(ToastXml.CreateTextNode("Woot Woot!!!"));

    //create the toast
    var Toast = new ToastNotification(ToastXml);

    //show the toast in the top right corner
    ToastNotificationManager.CreateToastNotifier().Show(Toast);

}

now when you click the ToastButton a little notification will appear in the top right corner containing our text.

Now we can actually add a Failed, Dismissed and activated handlers to our toast for some richer interaction

//create the toast
var Toast = new ToastNotification(ToastXml);

Toast.Activated += Toast_Activated;

and then create an event handler to do some stuff

void Toast_Activated(ToastNotification sender, object args)
{
    //Do some stuff
}

in short that's about it, pretty straight forward.