Windows Phone 8.1 for Developers–Geolocation and Geofencing

This blog post is part of a series about how Windows Phone 8.1 affects developers. This blog post talks about how to use geolocation and geofencing and is written by Andreas Ekberg at Jayway and was originally posted here.

Geofencing is a new cool feature in windows phone 8.1 which makes it possible for you to be notified when you enter or leave a specific location, optionally within a specific time. You can trigger code to run in the background or if your app is running, get an event. In the end of the article we will additionally cover the minor changes to the Geolocation API that includes classes related to positioning.

Geofencing

The new geofencing feature in WP8.1 can enable many new application ideas. The most obvious is to display a reminder with e.g. a shopping list when you enter a store, but you can use it as a base for a tour guide app or why not connect it to your intelligent home and unlock the door and light up the house when you park your car. First, to be able to use geofencing (as well as geolocation) you need to set the Location Capability in the App Manifest. There is also a good practice to add a call to Geolocator.GetGeopositionAsync() early in your app to trigger the popup question that asks the user for permission the first time a location related method is called. Geofencing is found in the namespace Windows.Devices.Geolocation.Geofencing and the two main classes are Geofence and GeofenceMonitor. You create one Geofence object for each location you want to be notified about. All properties are set in the constructor and the required ones are the unique Id and a Geoshape to describe the area that triggers the notification. Geocircle is the only supported Geoshape at the moment, and it is basically a Geopoint with a radius. Additionally you can postpone the monitoring with the StartTime property. The Duration property is used to set how long you will monitor after the start time while DwellTime is used to only trigger events when you have been at the location for a certain time. If you want the Geofence to be removed after it has been triggered the first time, set SingleUse to true. Finally you can use the MonitoredStates to select which states you want to monitor: Entered, Exited and/or Removed. When you have created a Geofence you have to register it with the GeofenceMonitor. GeofenceMonitor is a singleton class and you use the static Current property to get its instance. To register the Geofence you simply add it to the Geofences collection and then add a listener to the GeofenceStateChanged event. When the event is triggered, ReadReports gives you data about what has changed since last time you called that Method. This includes fences triggered when the app was not running. Below is a code example that shows a dialogue when you have been within 400 m of the Jayway office in Malmö, Sweden for 10 seconds:

 private async Task Init_Geofence()
{
    var geofenceMonitor = GeofenceMonitor.Current;
    var loc = await new Geolocator().GetGeopositionAsync(
        TimeSpan.FromMinutes(2), 
        TimeSpan.FromSeconds(5));

    geofenceMonitor.GeofenceStateChanged += (sender, args) =>
    {
        var geoReports = geofenceMonitor.ReadReports();
        foreach (var geofenceStateChangeReport in geoReports)
        {
            var id = geofenceStateChangeReport.Geofence.Id;
            var newState = geofenceStateChangeReport.NewState;
            if (id == GeoId && newState == GeofenceState.Entered)
            {
                Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 
                    new MessageDialog("At Jayway, come in and say Hi!")
                    .ShowAsync());
            }
        }
    };

    var jayway = new Geopoint(new BasicGeoposition()
    {
        Latitude = 55.6127809826285, Longitude = 13.0031764693558
    });
    var geofence = new Geofence(GeoId, new Geocircle(jayway.Position, 400), 
        MonitoredGeofenceStates.Entered | MonitoredGeofenceStates.Exited, 
        false, TimeSpan.FromSeconds(10));
    geofenceMonitor.Geofences.Add(geofence);
}

If you want the geofence to trigger even when the app is not in the foreground, you can use a background task. First you call BackgroundExecutionManager.RequestAccessAsync() to ask the user to accept running in the background (the actual prompt is only visible one time). You then use the BackgroundTaskBuilder to create the background task, add a LocationTrigger and finally you register the task:

 private async Task Init_BackgroundGeofence()
{
    var backgroundAccessStatus = 
        await BackgroundExecutionManager.RequestAccessAsync();
    var geofenceTaskBuilder = new BackgroundTaskBuilder
    {
        Name = "GeofenceBackgroundTask",
        TaskEntryPoint = "BackgroundTask.GeofenceBackgroundTask"
    };

    var trigger = new LocationTrigger(LocationTriggerType.Geofence);
    geofenceTaskBuilder.SetTrigger(trigger);
    var geofenceTask = geofenceTaskBuilder.Register();
    geofenceTask.Completed += (sender, args) =>
    {
        //TODO: add foreground code if needed
    };
}

You probably want add code to prompt the user if the backgroundAccessStatus is not to satisfaction. You get an exception if you try to register a background task with a name that’s already been registered. If you are not sure if the backgroundtask has already been register you should first iterate through BackgroundTaskRegistration.AllTasks and verify. If you want to take special action when the app is in foreground you should not combine the geofenceMonitor.GeofenceStateChanged from the previous example with backgroundtasks. It's much better to use the geofenceTask.Completed from above. Additionally to get the background task to work you need to add a background declaration to the app manifest and implement the actual background task. In the app manifest you select the Declaration tab and selects Background Tasks and click Add under Available Declarations. Select Location under Properties and add the same text in Entry point as in the code above, e.g. BackgroundTask.GeofenceBackgroundTask manifestbg

To implement the background task you need to add a new project (named e.g. BackgroundTask if we use the exact code above) to the solution. Note! This must be a “Windows Runtime Component”! You also need a reference from your WP8.1 project to this newly created project. In the BackgroundTask project add a new class that inherit IBackgroundTask and add your background logic to the Run method.

 namespace BackgroundTask
{
    public sealed class GeofenceBackgroundTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            //TODO: Add background code here
        }
    }
}

In the background code you can use the GeofenceMonitor.ReadReports method just like in the foreground example. Note that the same geofence trigger can only be read once via ReadReports, so if you want logic both in background and foreground code you have to save some state in preferable the ApplicationData.Current.LocalSettings to communicate the geofence trigger info. You can also add entries to log-files, show toast or even trigger REST calls from you background code.

Geolocation

In windows phone 8.0 we had two namespaces with almost identical content, the old System.Device.Location and the new Windows.Devices.Geolocation. In 8.1 only Windows.Devices.Geolocation is left. If you want to update old apps using the System.Device.Location there are plenty of information on the internet. The 8.0 maps api used System.Device.Location.GeoCoordinate but 8.1 now uses Windows.Devices.Geolocation.Geopoint. More on that in my previous blog: Windows Phone 8.1 for Developers – Maps The only other changes in Windows.Devices.Geolocation are two additional classes and one extra property on Geocoordinate:

  • GeoboundingBox: Represents a rectangle that defines a geographic area. Replaces the LocationRectangle used in the 8.0 map api
  • Geopath: Represents an ordered series of geographic points and is used for example when you create a MapPolygon.
  • Geocoordinate.PositionSourceTimestamp: Gets the time at which the associated Geocoordinate position was calculated, and may be completely unrelated to the system time on the device. E.g. if the position is obtained from GPS, the timestamp would be obtained from the satellites.

Over and Out!