Another type of agent you should be aware of
is the location-aware applications. Unlike the background agents, this
type of app continues to run in the background when a user navigates
away from it. In this mode, the number of APIs you can use is minimal
and a special geolocation class can be used to be notified when a user
changes location. This type of app is specifically for apps that want to
track the user (for instance, jogging and bike applications are
common).
To create a location-aware app, you must also set up the WMAppManifest.xml
file to tell the phone that you want to be able to run in the background. To do this, you have to add the ID_CAP_LOCATION
capability. In addition, you have to manually edit the WMAppManifest.xml
file to include a special option to run as a location-aware application. If you open the WMAppManifest.xml
file as text, you’ll find the DefaultTask
element inside the Task
element. Inside that element you will need to add a new element called BackgroundExecution
:
<Deployment ...>
<App ...>
...
<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml">
<BackgroundExecution>
<ExecutionType Name="LocationTracking" />
</BackgroundExecution>
</DefaultTask>
</Tasks>
...
</App>
</Deployment>
The ExecutionElement
’s name must be LocationTracking
for this to work. With both of those changes made, you can now create a
location-aware application. But how do you know when your app is
running in the background?
If you open the App.xaml
file, you’ll find the PhoneApplicationService
element that contains event handlers for the four main application
events (for instance, Launching, Activated, and so on). In a
location-aware app, you can also handle the RunningInBackground
event as shown here:
<shell:PhoneApplicationService Launching="Application_Launching"
Closing="Application_Closing"
Activated="Application_Activated"
Deactivated="Application_Deactivated"
RunningInBackground="Application_RunningInBackground" />
Because this is another state the application
can go into, you should communicate to the rest of your application that
you are in background mode. In this mode you can’t update the UI but
can do other work (such as updating a Live Tile or showing
notifications). A common approach is to have an App
class-level flag for when the application is running in background mode, like so:
// In app.xaml.cs
public static bool IsRunningInBackground { get; set; }
private void Application_Launching(object sender,
LaunchingEventArgs e)
{
}
private void Application_Activated(object sender,
ActivatedEventArgs e)
{
IsRunningInBackground = false;
}
private void Application_Deactivated(object sender,
DeactivatedEventArgs e)
{
}
private void Application_Closing(object sender, ClosingEventArgs e)
{
}
private void Application_RunningInBackground(object sender,
RunningInBackgroundEventArgs e)
{
IsRunningInBackground = true;
}
By changing the flag, you can then decide how to
handle location events in your application. But how do you get notified
of a change in location?
That is where the Geolocator
class comes in .
This class enables you to be notified via an event about a change in
location as well as being able to tell the class how far a user has to
move before you’re notified. This enables you to specify how much
granularity you need. A jogging app might require much more granularity
than a driving app, for example.
Typically, the App
class exposes a property that will hold an instance of the Geolocator
class:
// In App.xaml.cs
public static Geolocator Locator { get; set; }
This way, in the page responsible for starting
the geolocation, you can simply create an instance. For example, when a
user clicks a start button, you could simply do the following:
private void startButton_Click(object sender, EventArgs e)
{
if (App.Locator == null)
{
App.Locator = new Geolocator()
{
DesiredAccuracy = PositionAccuracy.High,
MovementThreshold = 100 // Meters
};
App.Locator.PositionChanged += Locator_PositionChanged;
}
}
When you create an instance to the Geolocator
class, you specify how accurate the locator should be (same as the Normal and High accuracy from the GeoCoordinateWatcher
class). You can also specify how far (in meters) to use as a movement
threshold. In this instance, you’ll be notified only if the user has
moved a minimum of 100 yards.
Creating this instance and registering for the
event tells the class to notify you as the user moves. There are no
start and stop methods—just creating it is enough. To stop geolocation,
simply unregister the event and set the reference to null (to allow the
garbage collector to destroy it):
private void stopButton_Click(object sender, EventArgs e)
{
App.Locator.PositionChanged -= Locator_PositionChanged;
App.Locator = null;
}
Finally, you can handle the PositionChanged
event:
void Locator_PositionChanged(Geolocator sender,
PositionChangedEventArgs args)
{
var coordinate = args.Position.Coordinate;
if (App.IsRunningInBackground)
{
var title = string.Format("{0:F2} x {1:F2}",
coordinate.Longitude,
coordinate.Latitude);
var tileData = new FlipTileData()
{
Title = title
};
var tile = ShellTile.ActiveTiles.First();
tile.Update(tileData);
}
else
{
// Not happening on the UI thread
Dispatcher.BeginInvoke(() =>
{
DataContext = new
{
Long = coordinate.Longitude,
Lat = coordinate.Latitude,
Alt = coordinate.Altitude
};
});
}
}
Because this class will fire regardless of whether the application is running in the background, you can simply check the App.IsRunningInBackground
property you created earlier. This is where you will decide what to do
with the event. You should avoid updating the user interface when
running in the background. In this example, you update the Live Tile
with the coordinates instead of showing them in the UI.
Consequently, when you are running in the
foreground, you can update the user interface. This event won’t fire on
the UI thread, so you should use the Dispatcher to marshal the call back
to the UI thread, as shown in the call to BeginInvoke above.