I have a Windows 10 Universal app that's written in JavaScript. The app is a location tracker and needs to run in the background, and I am attempting to use the ExtendedExecution APIs to make that happen. I'm finding, though, that this works as advertised in a C#/XAML app, but does not work in a JavaScript app.
As an experiment, in Visual Studio 2015 I created a new C# project via File -> New -> Project -> Visual C# -> Blank App (Universal Windows)
and kitted it out as follows:
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private Geolocator locator;
private ObservableCollection<string> coordinates = new ObservableCollection<string>();
private ExtendedExecutionSession session;
public MainPage()
{
this.InitializeComponent();
// Start geo locating
locator = new Geolocator();
locator.DesiredAccuracy = PositionAccuracy.High;
locator.DesiredAccuracyInMeters = 0;
locator.MovementThreshold = 0;
locator.PositionChanged += positionChanged;
coords.ItemsSource = coordinates;
// Request extended execution
RequestExtendedExecution();
}
private async void RequestExtendedExecution()
{
// Request extended execution via the ExtendedExecution API
session = new ExtendedExecutionSession();
session.Description = "Location Tracker";
session.Reason = ExtendedExecutionReason.LocationTracking;
session.Revoked += ExtendedExecutionSession_Revoked;
var result = await session.RequestExtensionAsync();
if (result == ExtendedExecutionResult.Allowed)
coordinates.Insert(0, "Extended execution SUCCESS");
else if (result == ExtendedExecutionResult.Denied)
coordinates.Insert(0, "Extended execution DENIED");
else
coordinates.Insert(0, "Extended execution unexpected return code");
}
private async void EndExtendedExecution()
{
if (session != null)
{
session.Dispose();
session = null;
}
}
private void ExtendedExecutionSession_Revoked(object sender, ExtendedExecutionRevokedEventArgs args)
{
var _ = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
coordinates.Insert(0, "Extended execution REVOKED: " + ((args.Reason == ExtendedExecutionRevokedReason.Resumed) ? "Resumed" : (args.Reason == ExtendedExecutionRevokedReason.SystemPolicy) ? "Resources" : "Unknown reason"));
});
EndExtendedExecution();
}
private void positionChanged(Geolocator sender, PositionChangedEventArgs args)
{
var coord = args.Position;
string position = string.Format("{0},{1}",
args.Position.Coordinate.Point.Position.Latitude,
args.Position.Coordinate.Point.Position.Longitude);
var _ = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
coordinates.Insert(0, position);
});
}
}
The markup is simply:
<ListView x:Name="coords" />
This absolutely works as expected. When I request the extended session, I get "Extended execution SUCCESS", when minimized the app continues to add coords to the ListView, and when returned to the foreground I get "Extended execution REVOKED: Resumed". Super duper. Back to Visual Studio 2015, I then created a new JavaScript project via File -> New -> Project -> JavaScript -> Blank App (Universal Windows)
and implemented the same features as follows:
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var extendedExecution = Windows.ApplicationModel.ExtendedExecution;
var session = null;
var geolocator = null;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize your application here.
// Start geo tracking
Windows.Devices.Geolocation.Geolocator.requestAccessAsync().done(
function (accessStatus) {
switch (accessStatus) {
case Windows.Devices.Geolocation.GeolocationAccessStatus.allowed:
geolocator = new Windows.Devices.Geolocation.Geolocator();
geolocator.ReportInterval = 1000;
// Subscribe to PositionChanged event to get updated tracking positions
geolocator.addEventListener("positionchanged", function (e) {
var coord = e.position.coordinate;
log("app.onactivated: coord = " + coord.point.position.latitude + ", " + coord.point.position.longitude, false, true, false);
});
break;
case Windows.Devices.Geolocation.GeolocationAccessStatus.denied:
log("Geolocator.requestAccessAsync: Access to location is denied.", false, true, false);
break;
case Windows.Devices.Geolocation.GeolocationAccessStatus.unspecified:
log("Geolocator.requestAccessAsync: Unspecified error.", false, true, false);
break;
}
},
function (err) {
log("Geolocator.requestAccessAsync: error " + err, false, true, false);
}
);
// Request extended execution
requestExtendedExecution();
} else {
// TODO: This application was suspended and then terminated.
// To create a smooth user experience, restore application state here so that it looks like the app never stopped running.
}
args.setPromise(WinJS.UI.processAll());
}
};
app.oncheckpoint = function (args) {
// 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 args.setPromise().
log("app.oncheckpoint: application is about to be suspended");
};
function requestExtendedExecution() {
// If we already have an extended session, close it before requesting a new one.
if (session != null) {
session.close();
session = null;
}
// Request extended execution via the ExtendedExecution API
session = new extendedExecution.ExtendedExecutionSession();
session.description = "Background location tracking";
session.reason = extendedExecution.ExtendedExecutionReason.locationTracking;
session.onrevoked = function (args) {
log("requestExtendedExecution: Background mode revoked: " + args.reason);
requestExtendedExecution();
};
session.requestExtensionAsync().done(
function success() {
log("requestExtendedExecution: Successfully enabled background mode");
},
function error(error) {
log("requestExtendedExecution: Could not enable background mode: " + error);
}
);
}
function log (text) {
var now = new Date();
var timestamp = now.toLocaleDateString() + " " + now.toLocaleTimeString();
var outputDiv = document.getElementById("divOutput");
outputDiv.innerHTML = timestamp + " " + text + "<br/>" + outputDiv.innerHTML;
}
app.start();
})();
And the markup is:
<div id="divOutput"></div>
When I request the extended session, I still get "Extended execution SUCCESS", yay, but when I minimize the app, app.oncheckpoint gets called, the app gets suspended and there is no further activity until it returns to the foreground. I have also tried requesting the extended session from within app.oncheckpoint, but that has no effect either.
Anyone have some insight into this? Thanks in advance.
It works. The actual problem is your code doesn't listen on revoke event. It should be onrevoked. :)
And there are some small problems in your code.
Try the following:
function requestExtendedExecution() {
// Request extended execution via the ExtendedExecution API
session = new extendedExecution.ExtendedExecutionSession();
session.description = "Background location tracking";
session.reason = extendedExecution.ExtendedExecutionReason.locationTracking;
session.onrevoked = function (args) {
log("requestExtendedExecution: Background mode revoked: " + args.reason);
};
session.requestExtensionAsync().done(
function success() {
log("requestExtendedExecution: Successfully enabled background mode");
},
function error(error) {
log("requestExtendedExecution: Could not enable background mode: " + error);
}
);
}