The location sensor is a very
useful capability, given that mobile devices are generally on the go
with their owner. Location provides context for applications that can
make life easier on the user by automatically adjusting for the user's
current location. A great example is the Search button on the phone.
Enter or speak a keyword and click search. The local tab finds relevant
items nearby. So a search for "Starbucks" gives the user exactly what
they want on the local tab—which café is closest.
1. Understanding How It Works
The location sensor is a service
that can use cell tower locations, wireless access point locations, and
GPS to determine a user's location with varying degree of accuracy and
power consumption.
Determining location with GPS is
highly accurate, but it takes a while to spin up the GPS, and it
consumes relatively more battery. Determining location using cell tower
location is very fast, doesn't consume additional battery, but may not
be accurate depending on how many cell towers are in range and their
relative distance from each other in relation to the phone. Determining
location with wireless access points can be accurate depending on how
many wireless access points are in range and their relative position. If
only one wireless access point is available location data will have a
large error ring. Turning on Wi-Fi can consume additional battery power
as well.
2. Programming with Location
You may try to guess that the Location namespace is in the Microsoft.Devices.Sensors
namespace, but that would not be correct. It is located in the
System.Device.Location namespace. The primary class for location is the
GeoCoordinateWatcher class with the following class members:
DesiredAccuracy: Can have a value of GeoPositionAccuracy.Default or GeoPositionAccuracy.High. The latter value forces the use of GPS, which can delay readings and consume battery. Use with care.
MovementThreshold: This has a type of double. It indicates how far you have to move in order to generate a location reading in meters.
Permission: Level of access to the location service.
Position: Latest position obtained from location service.
PositionChanged: Event that fires when a new position is available.
Start: Starts location data acquisition from the location service.
Status: Current status of the location service.
StatusChanged: Event that fires when the status of the location changes.
Stop: Stops location data acquisition from the location service.
TryStart:
Attempts to start location data acquisition with a timeout parameter
passed in. The method returns false if the timeout expires before data
is acquired. This call is synchronous and will block the thread it is
called on – call on a background thread.
The sample in that solution for this section is titled LocationSensorSilverlight. In the MainPage() constructor for the LocationSensorSilverlight project, the LocationService is instantiated and events for PositionChanged and StatusChanged are wired up. Here is the code:
LocationService = new GeoCoordinateWatcher();
LocationService.PositionChanged +=
new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>
(LocationSensor_PositionChanged);
LocationService.StatusChanged +=
new EventHandler<GeoPositionStatusChangedEventArgs>
(LocationSensor_StatusChanged);
The rest of the application is just UI to display the LocationService information. The code uses the Bing Maps Map object to center the map on the user's current location. Here is the event handler to plot and zoom in on the map a bit:
private void PlotLocation_Click(object sender, EventArgs e)
{
BingMap.Center = LocationService.Position.Location;
BingMap.ZoomLevel = 15;
}
The UI implements the
Application Bar to start and stop the Location Service as well as plot
current location. The Application Bar also has a menu item to change the
location accuracy. It is not possible to change accuracy after
instantiating the GeoCoordinateWatcher variable. You have to instantiate a new GeoCoordinateWatcher variable and wire-up the event handlers again. Here is the code that handles this:
private void SetAccuracy_Click(object sender, EventArgs e)
{
if (LocationService.DesiredAccuracy == GeoPositionAccuracy.Default)
if (MessageBox.Show(
"Current Accuracy is Default. Change accuracy to High?"
+"This may take some time and will consume additional battery power.",
"Change Location Accuracy", MessageBoxButton.OKCancel)
== MessageBoxResult.OK)
{
LocationService.Dispose();
LocationService = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
LocationService.PositionChanged +=
new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>
(LocationSensor_PositionChanged);
LocationService.StatusChanged +=
new EventHandler<GeoPositionStatusChangedEventArgs>
(LocationSensor_StatusChanged);
}
else
if (MessageBox.Show(
"Current Accuracy is High. Change accuracy to Default?"+
"This wll be faster but will reduce accuracy.",
"Change Location Accuracy", MessageBoxButton.OKCancel)
== MessageBoxResult.OK)
{
LocationService.Dispose();
LocationService =
new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
LocationService.PositionChanged +=
new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>
(LocationSensor_PositionChanged);
LocationService.StatusChanged +=
new EventHandler<GeoPositionStatusChangedEventArgs>
(LocationSensor_StatusChanged);
}
}
The XAML for the MainPage
class implements a simple status panel that displays Location Service
data. The code also uses the Silverlight Toolkit GestureListener to
allow the user to drag the status panel over the map. Here is the
markup:
<Border x:Name="LocationStatusPanel" HorizontalAlignment="Left" VerticalAlignment="Top"
Background="#96000000" Padding="2" >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener DragDelta="GestureListener_DragDelta"/>
</toolkit:GestureService.GestureListener>
<Border.RenderTransform>
<CompositeTransform/>
</Border.RenderTransform>
<StackPanel Orientation="Horizontal" Width="200" >
...Xaml for TextBoxes here.
</StackPanel>
</Border>
Here is the GestureListener_DragDelta event handler code that repositions based on the user dragging the status panel.
private void GestureListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
{
Border border = sender as Border;
CompositeTransform compositeTransform = border.RenderTransform as CompositeTransform;
compositeTransform.TranslateX += e.HorizontalChange;
compositeTransform.TranslateY += e.VerticalChange;
e.Handled = true;
}
This
concludes the overview of the Location Service available for Windows
Phone 7. The next section covers how to capture Microphone input and
play it back.