Background task pollution

April 22, 2012 at 1:21 pm | Posted in HTML/JavaScript, Metro, Windows 8 | Leave a comment

I am developing a Metro HTML/JavaScript application which uses a background task to notify the user of updates to some data that is collected periodically. Initally, the background task simply read the data, did some filtering and sent a tile notification. This all seemed to be working well. When I added a toast notification; however, I began to see some strange behavior.

First, I noticed that when the background task fired based on a timer, that dozens of processes seemed to appear in the Visual Studio debugger. Second, I found that the toast was triggered multiple times for each interval. Of course, this continued even if the program was no longer running. It occurred after a reboot also.

My suspicion was that the background task was registered multiple times. My program was configured to register the background task every time it was launched. I assumed that the system would just overwrite the previous registration with the new one. It appears, though that each registration is added to a list of registrations even if they are identical. After numerous debug sessions, these registrations were adding up. I was polluting the background task manager.

The programmatic solution to this problem is to unregister all of the tasks associated with the program using the following routine:

function unregisterBackgroundTasks(taskName) {
        //
        // Loop through all background tasks and unregister any with the taskName
        //
        var iter = Windows.ApplicationModel.Background.BackgroundTaskRegistration.allTasks.first();
        var hascur = iter.hasCurrent;
        while (hascur) {
            var cur = iter.current.value;
            if (cur.name === taskName) {
                cur.unregister(true);                
            }
            hascur = iter.moveNext();
        }
    }

This seems to work well but I do not know if there is a way to clear this “pollution” of the background task manager outside of the program except that background tasks are unregistered when a program is uninstalled.

Advertisements

Updating Metro tiles in the background periodically

April 20, 2012 at 9:00 am | Posted in HTML/JavaScript, Metro, Windows 8 | 3 Comments

This is an example of how to use a background task to update an app’s tiles.

First, here is a default html file with a button and status line.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>HelpTileUpdate</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- HelpTileUpdate references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <button id="startService">Start Background</button>
    <div id="status">Status of service</div>
</body>
</html>

Next, the default.js which handles the button click event.   This handler requests to use the lock screen (required for timer background tasks) and then registers the task with a timeTrigger set for 15 minutes (the minimum allowed interval).

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
            if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension.
                // Restore application state here.
            }
            WinJS.UI.processAll().then(function onComplete(processedElements) {
                document.getElementById("startService").addEventListener("click", beginBackgroundService, false);
        });
        }
    };

    app.oncheckpoint = function (eventObject) {
        // TODO: This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. You might use the
        // WinJS.Application.sessionState object, which is automatically
        // saved and restored across suspension. If you need to complete an
        // asynchronous operation before your application is suspended, call
        // eventObject.setPromise().
    };

    app.start();

    function beginBackgroundService() {

        requestLockScreenAccess();
        registerBackgroundTask();
    }

    function displayStatus(msg) {
        document.getElementById("status").innerText = msg;
    }

    function registerBackgroundTask() {
        var builder = new Windows.ApplicationModel.Background.BackgroundTaskBuilder();
        builder.name = "myTask";
        builder.taskEntryPoint = "backgroundtask.js";
        var timeTrigger = new Windows.ApplicationModel.Background.TimeTrigger(15, false);
        //var timeTrigger = new Windows.ApplicationModel.Background.SystemTrigger(Windows.ApplicationModel.Background.SystemTriggerType.timeZoneChange, false);
        builder.setTrigger(timeTrigger);
        var conditionType = Windows.ApplicationModel.Background.SystemConditionType.internetAvailable;
        var taskCondition = new Windows.ApplicationModel.Background.SystemCondition(conditionType);
        builder.addCondition(taskCondition);
        var backgroundTaskRegistration = builder.register();

    }

    //
    // This is a helper function to add your app to the lock screen.
    // Time events require the app to be on the lock screen to fire
    //
    function requestLockScreenAccess() {
        var Background = Windows.ApplicationModel.Background;

        //
        // An app can call the add or query API as many times as it wants; however, it will only present the dialog box to the user one time.
        //
        Background.BackgroundExecutionManager.requestAccessAsync().then(function (result) {
            switch (result) {
                case Background.BackgroundAccessStatus.denied:
                    displayStatus("This app is not on the lock screen.");
                    break;

                case Background.BackgroundAccessStatus.allowedWithRealTimeConnectivity:
                    displayStatus("This app is on the lock screen and has access to Real Time Connectivity.");
                    break;

                case Background.BackgroundAccessStatus.allowedWithoutRealTimeConnectivity:
                    displayStatus("This app is on the lock screen, but does not have access to Real Time Connectivity.");
                    break;

                case Background.BackgroundAccessStatus.unspecified:
                    displayStatus("The user has not yet taken any action. This is the default setting and the app is not on the lock screen.");
                    break;
            }
        }, function (e) {
            displayStatus(e);
            console.log(e);
        });
    }
})();

Next, you will need to add a backgroundtask.js file to your project. This code simply checks the date and writes it to a wide tile.

//Background task
//Checks for new events and notifies user.
//Updates tiles

(function () {
    "use strict";

    importScripts("//Microsoft.WinJS.0.6/js/base.js");

    //Begin task
    var backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;

    var notifications = Windows.UI.Notifications;

    var date = new Date();

    displayTileNotification(date);
    backgroundTaskInstance.succeeded = true;
    close();

    function displayTileNotification(content) {
        // get a filled in version of the template by using getTemplateContent
        var tileXml = notifications.TileUpdateManager.getTemplateContent(notifications.TileTemplateType.tileWideText01);

        // get the text attributes for this template and fill them in
        var tileAttributes = tileXml.getElementsByTagName("text");

        var title = "Tile Updated At";
        tileAttributes[0].appendChild(tileXml.createTextNode(title));
        tileAttributes[1].appendChild(tileXml.createTextNode(content));

        // create the notification from the XML
        var tileNotification = new notifications.TileNotification(tileXml);

        // send the notification to the app's default tile
        notifications.TileUpdateManager.createTileUpdaterForApplication().update(tileNotification);
    }

})();

Finally, you need to configure the manifest. Double-click package.manifest to bring up the wizard.

  1. In the Application UI tab, change Lock screen notifications to Badge and Tile Text
  2. Supply pointers to a wide tile image and badge image of the required sizes.
  3. In the Declarations tab, add a Background Task
  4. Select Timer
  5. Add backgroundtask.js in the field for Start Page

Run the program.  Click the button and the first time, you will be prompted to enable lock screen access.  Allow it.

Watch the Start Screen.  Find your tile and change it to wide format.  Wait.  The tile should update in 15-30 minutes then every 15 minutes thereafter even if the program is terminated.

 

 

Tip for debugging JavaScript metro apps

April 5, 2012 at 7:52 am | Posted in HTML/JavaScript, Metro, Visual Studio, Windows 8 | Leave a comment

With the release of the Windows 8 beta, I’ve been playing around again with metro apps made with HTML/JavaScript.  I was frustrated with debugging situations in which portions of code just didn’t seem to execute while displaying no error messages.  Then yesterday, I discovered this.  It explains that code with Promises deals with runtime errors in a special way and expects error handling routines to react to appropriately.

It is helpful to make sure that the Visual Studio menu item Debug > Exceptions > JavaScript Runtime > Thrown is selected.  This should expose errors that the runtime has detected but is ‘holding’ for some handling routine that is often not yet implemented.

Using WMP and Quicktime in Silverlight (Revisited)

November 6, 2011 at 10:34 am | Posted in Silverlight | 1 Comment

In 2007, I was working on a Silverlight application in which I wanted to change the rate of speech playback without affecting the pitch.  At that time, the media element available to Silverlight did not have a rate control while Windows Media Player works wonderfully for mp3 files.  I found that I could actually use WMP in a Silverlight application by embedding the player in the HTML file that hosted the Silverlight control and then controlling the player by using a bridge between managed C# and JavaScript.  The technique was documented in this blog post.

Since then, changes to Silverlight and browsers that have broken this code.

I resurrected the old code base and ported it to Silverlight 4.  The code can be tested here.  And you can download a zip file of the web project here.  The code is still a bit dated and ‘hackish’ but has been tested on IE9, Chrome 15 and Firefox 3.6 with the WMP plug-in installed.  For some reason, the volume control does not work in Chrome.  I also forced Chrome to use the Quicktime player instead of WMP.  It works, but Quicktime does not preserve the pitch when the rate is changed so it will sound weird at slow speeds.  I did not test on a Mac.

Silverlight 5 is supposed to have a new media element that has a rate control function.  If it works as well as WMP, then this approach will no longer be necessary.

Using maps in a Windows 8 Metro application

October 29, 2011 at 2:46 pm | Posted in HTML/JavaScript, Metro, Windows 8 | Leave a comment

With the release of the Customer Preview of Windows 8, there is now a Bing Maps SDK which can be used with both C# and JavaScript apps.  This functionality makes the technique described in this post unnecessary. (Added 4/10/12)

There are many uses for detailed, interactive maps in mobile and desktop applications.  The basic and advanced mapping functionality can be supplied by Google or Bing map APIs.  This post will show how to integrate these maps into a Windows 8 Developer Preview application.

Currently, there is no map control for Metro development so one solution is to use a JavaScript map API in an element that can be hosted in the application.  Security restrictions make this approach tricky and, as far as I know, it is only possible to do this using the HTML/JavaScript programming environment.

The security issue is that the main application in the local context may not load scripts that are external to the host. Of course, for both Google and Bing map APIs require an external script load.  The solution is to use an iframe element in the body of the main app that refers to a local html page that operates in a web context and is able to load foreign scripts.  The postMessage() method is used to communicate between the two pages.

In the following example a simple application displays a map, a button and a status output.  The Google map API is used but it could easily be converted to use the Bing map API.  The map displays two markers: one on Paris and the other, Rouen, France.  Clicking the button sends a command to the map page.  The command is to zoom to a set of coordinates (for Paris).  If you click on one of the markers on the map, the city name is set back to the parent page (the main app) and is displayed in the status output.

For purposes of this example, the messaging system is very simple.  It would be possible to create a more generalized function that could be used for a variety of such applications.

Here are the main files:

First, the default.html containing the main display.  There is an iframe element which refers to another local file, map.html.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Map Help Example</title>
    <!-- WinJS references -->
    <link rel="stylesheet" href="/winjs/css/ui-dark.css" />
    <script src="/winjs/js/base.js"></script>
    <script src="/winjs/js/ui.js" type="text/javascript"></script>
    <script type="text/javascript" src="winjs/js/binding.js"></script>
    <script src="/winjs/js/animations.js" type="text/javascript"></script>
    <script src="/winjs/js/uicollections.js" type="text/javascript"></script>
    <script src="/winjs/js/wwaapp.js"></script>
    <!-- WinWebApp2 references -->
    <link rel="stylesheet" href="/css/default.css" />
    <script src="/js/default.js"></script>

</head>
<body>
    <div id="appBody">
        <h1 id="head_title" class = "win-title">
            Map Example</h1>
        <div id="status">
            Hello</div>
        <button type="submit" id="paris">Paris</button>
        <div id="mapSection">
            <iframe id="mapIframe" src="ms-wwa-web:///map.html"></iframe>
        </div>
    </div>
</body>
</html>

This is the map.html which holds the container for the actual map.  In addition to the map.js script, it loads the Google map API.

<!--This file is used to render the map API and will be included in an iFrame of the main html.-->
<!DOCTYPE html>
<html>
<head>

    <title>Map</title>
    <link href="winjs/css/ui-light.css" rel="stylesheet" type="text/css" />
    <!-- We can include remote scripts because it is rendered in the web context (ms-wwa-web:///map.html)  -->
    <script src="http://maps.googleapis.com/maps/api/js?sensor=false" type="text/javascript"></script>
    <script src="js/map.js" type="text/javascript"></script>
    <style type="text/css">

        #map
        {
          position:absolute;
          width:100%;
          height:100%;
        }

    </style>
</head>
<body>
    <div id="map"></div>
</body>
</html>

Here is the default.js which manages the main program UI. Note that the command is an object that is serialized to a string so that it can be transmitted to the map using postMessage(). There is also a function to receive a simple message from the map page to display on the status line.


(function () {
    'use strict';

    Debug.enableFirstChanceException(true);

    function id(elementId) {
        return document.getElementById(elementId);
    }

    WinJS.Application.onmainwindowactivated = function (e) {
        if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {

            WinJS.UI.processAll();

            //Button event listener
            paris.addEventListener("click", changeLocation, false);
        }
    }

    WinJS.Application.start();

    //Button event handler which sends a command to the map to zoom to Paris.
    function changeLocation(e) {

        id("status").innerText = "Zooming to Paris";

        //Construct a message to send
        var msg = {
            command: 'zoomTo',
            latitude: 48.863811,
            longitude: 2.351761,
            zoom: 8
        };

        //Convert message object to string and send to the map control.
        var msgS = JSON.stringify(msg);
        document.frames['mapIframe'].postMessage(msgS, "ms-wwa-web://" + document.location.host);
    }

    //Receive message from map and displays to status output
    window.addEventListener("message", receiveMessage, false);
    function receiveMessage(event) {
        if (event.origin === "ms-wwa-web://" + document.location.host) {
            id("status").innerText = "Hello from " + event.data;
        }
    }
})();

Here is the map.js which controls the map. It receives the string message and converts it back to an object where it is interpreted and operated on. Map events such as the clicking of a marker are handled by sending a message back to the parent page.


//This script handles all of the controls to the map API
(function () {
    'use strict';

    var map;

    //Process messages from main script
    window.addEventListener("message", receiveMessage, false);
    function receiveMessage(event) {
        if (event.origin === "ms-wwa://" + document.location.host) {

            //Return the message string to an object
            var messageObject = JSON.parse(event.data);

            //If message is to zoom, change the location and zoom level
            if (messageObject.command == "zoomTo") {
                var newCenter = new google.maps.LatLng(messageObject.latitude, messageObject.longitude);
                var newOptions = {
                    zoom: messageObject.zoom,
                    center: newCenter,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                };
                map.setOptions(newOptions);
            }
        }
    }

    //This function sends a message back to the parent window.
    function sendMessageBack(city) {
        window.parent.postMessage(city, "ms-wwa://" + document.location.host);
    }

    function initialize() {
        //initialize the map
        var latlng = new google.maps.LatLng(38.96, -96.78);
        var myOptions = {
            zoom: 4,
            center: latlng,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        map = new google.maps.Map(document.getElementById("map"),
        myOptions);

        //Add a couple of pushpins with event handlers
        var parisLocation = new google.maps.LatLng(48.863811, 2.351761);
        var marker1 = new google.maps.Marker({
            position: parisLocation,
            map: map,
            title: "This is Paris"
        });
        google.maps.event.addListener(marker1, 'click', function () {
            sendMessageBack("Paris");
        });

        var rouenLocation = new google.maps.LatLng(49.4467, 1.085889);
        var marker2 = new google.maps.Marker({
            position: rouenLocation,
            map: map,
            title: "This is Rouen"
        });
        google.maps.event.addListener(marker2, 'click', function () {
            sendMessageBack("Rouen");
        });
    }

    document.addEventListener("DOMContentLoaded", initialize, false);
})();

Finally, here is the default.css file which defines the layout of the main application elements.


#appBody
{
    width: 100%;
    height: 100%;
    display: -ms-grid;
    -ms-grid-rows: 100px 85px 520fr 60px;
    -ms-grid-columns: 42px 405px 9px 846fr 64px;
}

#head_title
{
    margin-bottom: -8px;
    margin-top: 0px;
    -ms-grid-column: 2;
    -ms-grid-row-align: end;
    -ms-grid-column-span: 5;
}

#mapSection
{
    -ms-grid-column: 4;
    -ms-grid-row: 1;
    -ms-grid-row-span: 4;
    -ms-grid-column-span: 2;
}

#mapIframe
{
    width: 100%;
    height: 100%;
}

#status
{
    -ms-grid-column: 2;
    -ms-grid-row: 2;
    -ms-grid-row-align: center;
}

#paris
{
    -ms-grid-column: 2;
    -ms-grid-row: 3;
    width: 59px;
    height: 35px;
}

Data binding made simple

September 1, 2011 at 9:51 pm | Posted in Silverlight, Uncategorized, WPF | Leave a comment

Data binding is a very important topic for WPF and Silverlight applications and Microsoft has created a very rich set of functionality in the frameworks to make it work in a variety of ways.  The problem is there are so many ways of doing things that I find the documentation from different sources to be quite confusing.

Today, I was working on a little application which should be very amenable to the use of data binding UI controls (listboxes and textboxes) to data objects.  After being away from WPF for a time, I was struggling to relearn exactly the best way to do it.  The MSDN documentation is thorough but is TMI at times.

I finally ran across a tutorial that captures the essence of what I needed to do in this application as well as others that I have worked on in the past.  If you want a clear example of how to implement data binding for either Silverlight or WPF, I recommend you check out this post. 

« Previous PageNext Page »

Blog at WordPress.com.
Entries and comments feeds.