September 2014

Volume 29 Number 9

Microsoft Store : Creating a Location-Aware App with Geofencing

Tony Champion

The ever-increasing adoption of mobile devices is propelling the creation of location-aware apps. The opportunities for apps that know and react to a user’s location are virtually limitless. Windows 8 included geolocation from the beginning, giving developers an easy way to determine the current location of a device. The Windows simulator even includes support for testing this feature. With Windows 8.1, these APIs have been extended with the concept of geofencing.

A geofence is a defined region around a GPS location that can be registered with Windows so an app can receive notifications when the device enters or leaves that region. Let’s say you’re a service technician with a dozen house calls to make in a given day. What if the app that manages your appointments could automatically send a text message to the home owner when you were five minutes away? Or suppose an amusement park wanted to announce the presence of characters, but only to people within a certain proximity of the character to limit the number of people that might show up. The possibilities are endless.

This article will explore the use of geofences in Windows 8.1. However, the geofencing API is also shared with Windows Phone 8.1, which means you can implement the same features on both platforms. You’ll learn how to add geofences to your location-based app, plus how to handle events when the app is in the foreground and how to handle notifications when the app is in the background.

Adding Geolocation Support

Because geofences are part of the geolocation API in the Windows SDK, you must add support for this API to your app before you can use it. Luckily, Windows Store apps need very little setup to use geolocation. In fact, the only requirement is the package appxmanifest must include the Location capability. To add this, open the solution’s appxmanifest in the designer and check “Location” in the capabilities tab, as shown in Figure 1.

Enable Location Capability
Figure 1 Enable Location Capability

Once this capability has been added to your project, the app is able to access Windows location services through the geolocation API if given permission by the user. This capability also adds a Permissions section to the app’s Settings charm, which lets the user enable and disable access to the device’s location, as shown in Figure 2.

Permissions Settings
Figure 2 Permissions Settings

In addition to granting access to the location services on an individual app level, Windows can disable location services for the entire device. Location services are enabled by default in Windows, but users or systems administrators are free to change this by going to Control Panel | Hardware and Sound | Location Settings.

Because there are several different scenarios that can affect the app’s access to the device’s location, it’s important your app know when there’s a change to this access. The SDK lets the app monitor its access to the location services through an event exposed in the DeviceAccessInformation class in the Windows.Devices.Enumeration namespace. An instance of the DeviceAccessInformation class is created through a static method, CreateFromDeviceClass, which takes a DeviceClass enumeration that specifies the hardware device on which you want information. For the location services, this value is DeviceClass.Location. After creating an instance of DeviceAccessInformation, you can determine the current access level from the CurrentStatus property and listen for access changes through the AccessChanged event, like so:

DeviceAccessInformation deviceAccess =
  DeviceAccessInformation.CreateFromDeviceClass(DeviceClass.Location);
DeviceAccessStatus currentStatus = deviceAccess.CurrentStatus;
// Setup geolocation based on status
// Listen for access changes
deviceAccess.AccessChanged += deviceAccess_AccessChanged;

The CurrentStatus property and AccessChanged event both receive a DeviceAccessStatus enumeration that describes the current access level. Figure 3 shows the available values and their definitions.

Figure 3 AccessChanged Event

private void deviceAccess_AccessChanged(
  DeviceAccessInformation sender, DeviceAccessChangedEventArgs args)
{
  switch (args.Status)
  {
    case DeviceAccessStatus.Allowed:
      // Access to the device is allowed
      break;
    case DeviceAccessStatus.DeniedByUser:
      // Denied by user when prompted or thru Permissions settings
      break;
    case DeviceAccessStatus.DeniedBySystem:
      // Denied at the system level from the Control Panel
      break;
    case DeviceAccessStatus.Unspecified:
      // Unknown reason for access to the device
      break;
  }
}

Determining Location

If you’re going to build a location-aware app that uses a geofence, you need to be able to determine the user’s location. Though this is not necessarily required to implement geofences, you’ll see later in this article that it comes in handy when validating geofence events.

The Windows SDK exposes a Geolocator class that attempts to determine the current GPS location of the device through several methods. The Geolocator and all location classes can be found in the Windows.Devices.Geolocation namespace. The first time you use a Geolocator class must be in the UI thread of the app, because Windows will prompt the user for permission to use the location services. Users are prompted only once per device and they can change this setting at any time by using the Permissions setting described earlier. Therefore, if a user blocks the original request or disables the permission from the Settings charm, you won’t be able to programmatically change the access back and must rely on a UI notification to prompt users to change it themselves.

To determine the device’s current location, Geolocator has a GetGeopositionAsync method that returns a Geoposition object. This object contains a Geocoordinate instance in the Coordinate property that contains the information returned by location services. Arguably the three most important pieces of information in a Geocoordinate class are the longitude, latitude and altitude of the current GPS position. In Windows 8.1, these are contained in the Point property, which is a Geopoint instance. The Latitude, Longitude and Altitude properties of the Geocoordinate class are there for backward compatibility, but the Point property should be used going forward.

There are several other valuable pieces of information in the Geocoordinate class that are helpful in dealing with geofences. The most important is the Accuracy property, which defines the GPS accuracy in meters. The Geolocator class uses the Windows location service to determine the current location. The location service uses several different approaches to do this. The most accurate method is when the device has a GPS radio that’s enabled and receiving a signal. If that’s not available, the location service tries using the device’s Wi-Fi connection to pinpoint the location. Finally, it tries a less accurate approach using IP resolution if the device doesn’t have an active Wi-Fi connection. Obviously, the accuracy of the current location is a significant factor in the validity of the geofences in your app and should be taken into consideration. For instance, IP resolution is only accurate to within 10 miles. That really won’t help much if you’re using geofences in an amusement park. With such a wide range of accuracy for a device, it’s important to consider the size of your geofence in comparison to the accuracy of the location to determine if the geofence is valid.

The Timestamp property is extremely useful in determining how old the data is. When dealing with geofences, timing can be very important. If you have geofences in your amusement park, for example, your app might use a different workflow if a user enters the parking lot during business hours versus the middle of the night. Figure 4 shows an example of getting the current location of the user’s device.

Figure 4 Getting Geoposition

Geolocator geo = new Geolocator();
try
{
  Geoposition pos = await geo.GetGeopositionAsync();
  double longitude = pos.Coordinate.Point.Position.Longitude;
  double latitude = pos.Coordinate.Point.Position.Latitude;
  double altitude = pos.Coordinate.Point.Position.Altitude;
  double accuracy = pos.Coordinate.Accuracy;
  DateTimeOffset timestamp = pos.Coordinate.Timestamp;
}
catch (Exception ex)
{
  // Handle errors like unauthorized access to location services
}

Creating Geofences

There are currently four different constructors for the Geofence class that allow you to define the location and behavior of a geofence. All of the geofencing structures are found in the Windows.Devices.Geolocation.Geofencing namespace. It’s important to know how you want the Geofence instance to behave prior to creating it, because all of the exposed properties are read-only. Therefore, if you don’t specify something during construction, you’ll have to replace the geofence with a new one if you want to modify it.

The minimum information you need to create a Geofence class is a unique string identifier and a definition of the shape of the geofence, which is represented by an implementation of the IGeoshape interface. The only shape supported in Windows 8.1 is the Geocircle, which is defined by a center location and a radius in meters. As with the Geofence, these values must be defined at construction and can’t be changed afterward. The radius can be anywhere from .1 meters to .25 of the earth’s circumference, which should be sufficient to handle any size your app requires. The following example shows how to create a Geofence at the current device’s location that has a 50-meter radius:

Geolocator geo = new Geolocator();
try {
  Geoposition pos = await geo.GetGeopositionAsync();
  Geocircle shape = new Geocircle(pos.Coordinate.Point.Position, 50.0);
  Geofence geofence = new Geofence("myUniqueId", shape);
}
catch (Exception) { }

Windows can monitor several different device interactions with a geofence. By default, it will monitor when a device enters and exits the defining area of the geofence. In addition, you can specify notification when a geofence has been removed from being monitored. It’s a requirement that either the entering or exiting state be monitored, so you can’t monitor only the removal of a geofence, which, to be honest, wouldn’t be very useful to your app in the first place. Monitoring states can be defined using a combination of MonitoredGeofence­States enumerations and are stored in the MonitoredStates property.

A geofence can also be a single- or multi-use entity. It’s considered to be used once all of the monitored states have occurred. Therefore, if you specify the entered and exit states, the device must enter and then leave the specified area before the geofence is considered used. Unless otherwise specified, the monitoring will continue until the geofence is removed. The SingleUse property identifies whether the Geofence is set for single use. The following builds on the previous example and shows the second Geofence constructor:

MonitoredGeofenceStates monitor = MonitoredGeofenceStates.Entered |
                                  MonitoredGeofenceStates.Exited |
                                  MonitoredGeofenceStates.Removed;
bool singleUse = true;
Geofence geofence = new Geofence("myUniqueId", shape, monitor, singleUse);

By default, a device must remain on the monitored side of a boundary for 10 seconds before an app is notified. This keeps the system from firing multiple events if a device is right on the edge and moving back and forth. This value is the DwellTime and can be set to any TimeSpan greater than 0. The same DwellTime will be used for entering and exiting the area. Therefore, if you need different values, you’ll have to create two geofences, one for entering and one for exiting. The following uses the third constructor by setting the DwellTime to 20 seconds:

TimeSpan dwellTime = new TimeSpan(0, 0, 20);
Geofence geofence = new Geofence("myUniqueId", shape, monitor,
  singleUse, dwellTime);

The final constructor allows you to set the StartTime and Duration of the geofence. A geofence will become active once the StartTime is in the past. By default, the StartTime is set to 0 or the beginning of a time handled in a DateTime class. If a geofence is created with a StartTime in the past and the device is already inside the defining area, the Entered state will be reported once the DwellTime has passed. In conjunction with the StartTime, you can define a Duration for how long the geofence will be active from the StartTime. If the Duration is set to 0 (the default value), the geofence remains active as long as it’s registered to be monitored. When a Duration value has been set, the app will be notified of its expiration if the Removed monitor state is selected. Here’s the final constructor, which sets a StartTime for midnight, Jan. 1, 2015, and a duration of 365 days:

DateTime startTime = new DateTime(2015, 1, 1);
TimeSpan duration = new TimeSpan(365, 0, 0, 0, 0);
Geofence geofence = new Geofence(
  "myUniqueId", shape, monitor, singleUse, 
  dwellTime, startTime, duration);

Using Geofences in the Foreground

After a geofence has been created, the next step is to register it to be monitored so your app can receive notifications about it. This is handled through a GeofenceMonitor instance. Each app will have a single GeofenceMonitor that can be accessed through the static property of GeofenceMonitor.Current. The GeofenceMonitor maintains a list of all registered geofences in the Geofences property, which is an IList<Geofence>. Adding and removing geofences from this list is as easy as using the IList methods you’re used to, such as Add and Remove. Once an app registers a geofence, it’s persisted to disk. This means you only need to register the geofence once, even between app uses. If you attempt to register a geofence with a duplicate id, an error will be generated, so it’s a good policy to verify the id isn’t present prior to registration:

IEnumerable<Geofence> existing =
  GeofenceMonitor.Current.Geofences.Where(g => g.Id == geofence.Id);
if (existing.Count() == 0)
{
  GeofenceMonitor.Current.Geofences.Add(geofence);
}
else
{
  // Handle duplicate entry
}

Once the geofence has been added to the GeofenceMonitor, you’ll receive notifications about it based on the monitor states that have been selected. These notifications are handled through the GeofenceStateChanged event of the GeofenceMonitor:

GeofenceMonitor.Current.GeofenceStateChanged += GeofenceStateChanged;

When the GeofenceStateChanged event is fired, the affected geofences are not sent in the args property, which is common for most changed event handlers. To get the notification about what has changed, you call the ReadReports method of the current GeofenceMonitor. This will return a collection of recent monitor notifications, sorted in a descending timestamp order, that have happened for the app. Each notification is represented by a GeofenceStateChangeReport class.

The GeofenceStateChangeReport has a Geofence property that references the geofence whose state has changed, and a Geopostion property that provides the location of the device responsible for the state being changed. It also has a NewState property, which is a GeofenceState enum that identifies which monitoring state was triggered. Finally, there’s a RemovalReason property, which is a GeofenceRemovalReason enum that can be either Used or Expired. The default is Used for any event and doesn’t mean the geofence has been removed. It simply provides a removal reason if the NewState value is Removed.

The number of reports returned by ReadReports is controlled by Windows. There’s no guarantee how many reports will be stored or for how long, so if you need to maintain any information, you’ll need to keep a separate copy in the app. Figure 5 is an example of handling the GeofenceStateChanged event.

Figure 5 GeofenceStateChanged Event Handler

void Current_GeofenceStateChanged(GeofenceMonitor sender, object args)
{
  IReadOnlyList<GeofenceStateChangeReport> reports =
    GeofenceMonitor.Current.ReadReports();
  foreach (GeofenceStateChangeReport report in reports)
  {
    switch (report.NewState)
    {
      case GeofenceState.Entered:
        // Handle entered state
        break;
      case GeofenceState.Exited:
        // Handle exited state
        break;
      case GeofenceState.Removed:
        if (report.RemovalReason == GeofenceRemovalReason.Used)
        {
          // Removed because it was single use
        }
        else
        {
          // Removed because it was expired
        }
        break;
    }
  }
}

Using Geofences in the Background

One of the additional issues developers have to take into consideration is the lifecycle of a Windows Store app. If an app isn’t visible on the screen, Windows suspends the application while keeping it in memory, in order to help preserve performance and battery life. This means your app will no longer be running. In most cases this isn’t a cause for concern because if the user isn’t directly interacting with the app, there’s nothing for the app to do.

However, in some scenarios, an app still needs the ability to continue to perform certain tasks, even if the app isn’t running. Windows addresses this with the concept of background tasks. A background task is a small unit of code that executes in response to a predefined system event. There are more than a dozen different events your app can register a background task for, and listening to geofences is one of them.

Adding a Background Task

All background tasks are implemented by creating sealed classes that implement the IBackgroundTask interface located in the Windows.ApplicationModel.Background namespace. This interface defines a single Run method that must be implemented. Any classes that implement IBackgroundTask must exist in a Windows Runtime Component and won’t execute if they’re part of your main app project. It’s common practice to put all background tasks for an app in a single Windows Runtime Component.

To create a background task, add a Windows Runtime Component project to your solution. Add a reference to that new project to your main app project so when you register the task the app has visibility to the class structure. Figure 6 illustrates a background task that responds to geofence state changes.

Figure 6 Background Task

public sealed class MyBackgroundTask : IBackgroundTask
{
  public void Run(IBackgroundTaskInstance taskInstance)
  {
    BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
    var reports = GeofenceMonitor.Current.ReadReports();
    foreach (var report in reports)
    {
      // Handle each report
    }
    deferral.Complete();
  }
}

Registering a Background Task

Once you’ve created a background task, the next step is to register that background task with Windows. As with location services, the user must grant your app permission to use background tasks. You can prompt the user via the static method RequestAccessAsync of the BackgroundExecutionManager class. If the user has already answered the prompt, this method will return the current status of running background tasks on the app.

The actual registration is completed through an instance of BackgroundTaskBuilder. It requires three pieces of information to be valid. The first is a unique name for the app. The second is the TaskEntryPoint property that takes the full name of the background task class in a string, including the namespace.

The last piece of information defines the type of event for which you want to register. This is done by creating a trigger and using the SetTrigger method of the BackgroundTaskBuilder. Every background task can have just a single trigger. If you want to use the same background task for multiple triggers, you must create and register multiple background tasks. In order to track geofence changes, create a LocationTrigger instance and pass in a LocationTriggerType.Geofence enum value into the constructor. Currently geofencing is the only location-based trigger offered in Windows. The end result is that the Run method of the IBackgroundTask you created will be called every time there’s a change to the state of a registered geofence. Figure 7 demonstrates how to register a geofence background task.

Figure 7 Register Background Task

private async void RegisterBackgroundTask(object sender, RoutedEventArgs e)
{
  BackgroundAccessStatus accessStatus =
    await BackgroundExecutionManager.RequestAccessAsync();
  if(accessStatus ==
    BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity ||
   accessStatus ==
     BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity)
  {
    BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder();
    taskBuilder.Name = "MyGeoBackground";
    taskBuilder.TaskEntryPoint = 
      "WindowsRuntimeComponent1.MyBackgroundTask";
    LocationTrigger trigger = new LocationTrigger(LocationTriggerType.Geofence);
    taskBuilder.SetTrigger(trigger);
    taskBuilder.Register();
  }
}

The final step to enable your app to run background tasks is to add a Background Task declaration in the package appxmanifest. Inside the appxmanifest designer, select the Declarations tab, select Background Task from the Add Declarations dropdown, then click the Add button. In the details pane, select Location as the property and for the Entry point, put the classname of the TaskEntryPoint of your BackgroundTaskBuilder. Once you do this, you’ll see a red “X” on the Application tab. Some background tasks require your app to be added to the lock screen in order for the tasks to run, and a location background task is one such task. The first step is to set the Lock screen notifications on the Application pane to either Badge or Badge and Tile Text. The next step is for the user to add the app to his lock screen. The BackgroundExecution­Mananger.Request­AccessAsync method discussed earlier will add the app to the lock screen if the user approves it. However, you need to write code to notify the user in the event the user removes the app from the lock screen at a later date, because Windows only prompts the user to add the app a single time.

Your app will now respond to geofence state changes in the background. It’s important to remember that this will take place even if your app is currently running, so the workflow of your app should take that into consideration if you’re also monitoring geofences in the foreground for UI updates.

For a deeper description of background tasks in Windows 8.1, please refer to “Supporting Your App with Background Tasks (XAML)” at bit.ly/1i9AH8X.

Wrapping Up

Geofences add a new dynamic to any location-aware app, by letting your app respond to changes in proximity to predefined GPS coor­dinates. This allows you to easily provide hot spots your app can interact with around the globe. Not only can your app respond to these geofences when it’s running, but by utilizing background tasks it can still respond to them when the app has been suspended or isn’t even running. With smaller GPS-enabled devices becoming ever more popular, adding location-aware features to your app can provide a great UX, and I, for one, can’t wait to see what gets created next.


Tony Champion is president of Champion DS, is a Microsoft MVP, and is active in the community as a speaker, blogger, and author. He maintains a blog at tonychampion.net and can be reached at tony@tonychampion.net.

Thanks to the following Microsoft technical expert for reviewing this article: Robert Green