Many devices have the ability for us to obtain the location of the user in a very precise manner. Android provides a few means to access the location, including using the network or using Global Positioning System (GPS).
If we are designing an app that requires the user's location, such as finding things nearby, we can use LocationManager
to get the user's current location:
[assembly: UsesPermission( Manifest.Permission.AccessCoarseLocation)]
[assembly: UsesPermission( Manifest.Permission.AccessFineLocation)]
LocationManager
:manager = LocationManager.FromContext(this);
manager.IsProviderEnabled( LocationManager.GpsProvider);
var intent = new Intent( Settings.ActionLocationSourceSettings); StartActivity(intent);
var cached = manager.GetLastKnownLocation( LocationManager.GpsProvider);
ILocationListener
interface:public class MainActivity : Activity, ILocationListener { public void OnLocationChanged(Location location) { } public void OnProviderDisabled(string provider) { } public void OnProviderEnabled(string provider) { } public void OnStatusChanged( string provider, Availability availability, Bundle extras) { } }
OnLocationChanged
method will be invoked and we can access the various location properties:double latitude = location.Latitude; double longitude = location.Longitude;
RequestSingleUpdate
method:manager.RequestSingleUpdate( LocationManager.GpsProvider, this, null);
RequestLocationUpdates
method:manager.RequestLocationUpdates( LocationManager.GpsProvider, 0, 0, this);
manager.RequestLocationUpdates( LocationManager.NetworkProvider, 0, 0, this);
var criteria = new Criteria(); var provider = manager.GetBestProvider(criteria, false);
manager.RemoveUpdates(this);
We can also work backwards. If we have location coordinates, we can request information about what is at that location using the Geocoder
instance:
Geocoder
is available on the device:var isPresent = Geocoder.IsPresent;
Geocoder
is available, we can make a request based on latitude, longitude, and the number of results that we want by using the GetFromLocationAsync()
method:var geocoder = new Geocoder(this); var resultCount = 1; var addresses = await geocoder.GetFromLocationAsync( currentLocation.Latitude, currentLocation.Longitude, resultCount);
Address
objects that represent places at the given coordinates:Address address = addresses.FirstOrDefault(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < address.MaxAddressLineIndex; i++) { var line = address.GetAddressLine(i); if (!string.IsNullOrWhiteSpace(line)) builder.AppendLine(line); } string addressLines = builder.ToString();
string houseNumber = address.SubThroughfare; string roadName = address.Throughfare; string suburb = address.SubLocality; string city = address.Locality;
LocationManager
provides a uniform way to access a device's physical location from various providers such as cell towers, Wi-Fi, GPS data from other apps and services, and the GPS device itself. Different providers provide varying degrees of accuracy as well as differing power requirements.
GPS is the most accurate but also requires the most power. The network provider, which uses the cell towers and Wi-Fi, is less accurate but requires less power. Less accurate providers can be used if the app does not require a constant stream of updates or needs to consume minimal battery.
In order to access the location services, we need to ask for permission. There are two levels of permissions, fine and coarse. Fine location permission is required for the GPS provider and the passive provider, which uses GPS data collected by other apps. Coarse location permission is used when using the cellular and Wi-Fi location data.
Before requesting location data, we need to be sure that the provider is available and is enabled. We can request that the user enable certain providers if it is needed. Enabling location services can be achieved by showing the settings activity.
When listening for location data, we subscribe to the updates with an interface. In order to catch the updates, we implement the OnLocationChanged()
method. This method will be invoked each time there is a new location from one of the location providers. The Location
parameter contains several properties which we can use to determine the coordinates and many other location attributes as well as the accuracy and time of the particular result.
We can request updates in two ways: as a single response or as a continuous stream of new location updates. If our app only requires the location once-off or infrequently, we can use the RequestSingleUpdate()
method. This will only request a single location result. If we need a stream of updates, we use the RequestLocationUpdates()
method.
Both methods require a provider to use when requesting updates. We can request updates from multiple providers using the same receiver as each new location will contain details on where the update came from.
If we are requesting a single update, we supply the provider, the callback, and the thread we wish to invoke the callback on. If we want to use the current thread, we pass null
. Similarly, when requesting a stream of updates, we provide the same parameters, but with an additional two. The first additional parameter specifies how frequently, in milliseconds, to request a new update; passing a zero will return them as fast as possible. The second specifies how far, in meters, the device needs to have moved before updating the location.
If we want to find out what provider is the best available, we can pass an instance of Criteria
to the GetBestProvider()
method. This method tries to find the best provider that matches the given criteria, excluding those that the app does not have permission for. We can also specify what the criteria are for selecting a provider using the various properties on the Criteria
type. Some of these properties include Accuracy
and Power
as well as AltitudeRequired
and SpeedRequired
.
Once we have the location we need or want to cancel updates, we can use the RemoveUpdates
method to stop listening on a particular ILocationListener
instance.
We can get an accurate location from the sensors, but sometimes we need to get some idea of what addresses are at or near that location. To do this, we use Geocoder
.
Geocoder
can provide a collection of Address
types that represent the physical addresses that are found near the provided location. There are two ways which we can request addresses: by specific location coordinates using the GetFromLocationAsync()
method or by a string location name using GetFromLocationNameAsync()
.
Android provides an additional location provider through Google Play Services: Fused Location Provider is an optional alternative to the standard Android Location Service that automatically handles changes in provider status inside our app. It selects the best provider at any given moment. It also provides the ability to create location-aware apps that use geo-fencing.
More information on using Fused Location Provider can be found at: http://developer.xamarin.com/guides/android/platform_features/maps_and_location/location/.
We can also embed a map directly into our app. This can be used to provide a visual representation of the device's location as well as enable us to place markers on the map showing the locations of various objects.
More information on including maps in an app can be found at: http://developer.xamarin.com/guides/android/platform_features/maps_and_location/maps/part_2_-_maps_api.
One of the requirements for using an embedded map is that we need a Google Maps API key. More information on how to obtain one for our app can be found at: http://developer.xamarin.com/guides/android/platform_features/maps_and_location/maps/obtaining_a_google_maps_api_key/.