Geolocation In Mobile Apps Using Xamarin
A- Geolocation Implementation In Xamarin:
Implementing geolocation requires taking advantage of device-specific location services which help provide a location to a particular user at any given point in time. There are many use cases where geolocation in mobile applications can be implemented such as displaying weather data for a user’s location, finding restaurants nearby and navigating user for a destination.
Even if you are not developing any weather forecasting or mapping application there are many useful application of location services which includes location-specific features or pre-filling data entry forms for improving conversion rates.
B- How to implement Geolocation in Xamarin.Forms
Access to geolocation services from a portable class library or shared project has been once a very tough task. As all mobile operating system iOS, Android and Windows implement location services very differently, therefore, dependency injection or conditional compilation would be quite. Also, each of the providers implement it very different and with different level of complexity.
Plugins for Xamarin are libraries which expose a single set of APIs in order to access common device functionality across the platform iOS, Android, and Windows. This enhances the amount of code which can be shared across multiple platforms hence speeding and simplifying mobile development. Plugins like Geolocator for Xamarin and Windows are amongst the plugins available for Xamarin and Windows. Let’s learn few plugins which can help in incorporating geolocation in your application.
Don’t forget to check: Xamarin Forms And Entity Framework Core, and
Development Of Native UI Mobile Applications Using Xamarin Forms
B-1 Platform Setup
To your portable class library and to the platform-specific projects, add the Geolocator Plugin for Xamarin and Windows NuGet to your cross-platform portable class library. As the user’s location is private we have to configure the application so that it can ask the user if it is okay to access their current location.
B1.1 Android
For allowing an application to access location services we have to enable two Android permissions: ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION. For adding NuGet to the Android project directly, this should be enabled default by you. Make sure the following permissions are enabled:
If you are targeting Android Marshmallow (Android M) or above, users will also be prompted automatically for runtime permissions.
B1.2 iOS
Depending on whether you will be using geolocation or just at certain points in a user’s workflow, you either will need to add the key NSLocationWhenInUsageDescription or NSLocationAlwaysUsageDescription in your Info.plist. Along with this a new string entry for the key is required which describes exactly what you will be doing with the user’s location. For supporting background updates (iOS9+ only) you must also enable the AllowsBackgroundUpdatesproperty of the Geolocator. The uibackgroundmodes key with the location value is also required for background updates.
Check this out: Xamarin And iOS10 App Development – What’s New
B1.3 Windows
ID_CAP_LOCATION permission must be enabled.
B1.4 Getting current location using to use it in a Xamarin.Forms.Maps
After browsing almost all the threads about Xamarin.Forms.Labs and Geolocator to get the current location and to use it in a Xamarin.Forms.Maps, I’m about to give up. And I’m quite lost when I see the Geolocation sample of Xamarin.Forms.Labs. As you may guess, I’m a newbie in Xamarin, or so to give you the most handy and easiest solutions.
Install Xamarin.Forms.Labs 1.2.1-pre2 in the PCL, iOS and Android projects via NuGet.
public async Task<Xamarin.Forms.Labs.Services.Geolocation.Position> GetCurrentPosition() {
IGeolocator geolocator = DependencyService.Get();<br/>
Xamarin.Forms.Labs.Services.Geolocation.Position result = null;
if (geolocator.IsGeolocationEnabled) {
try {
if (!geolocator.IsListening)
geolocator.StartListening(1000, 1000);
var task = await geolocator.GetPositionAsync(10000);
result = task;
System.Diagnostics.Debug.WriteLine("[GetPosition] Lat. : {0} / Long. : {1}", result.Latitude.ToString("N4"), result.Longitude.ToString("N4"));
}
catch (Exception e) {
System.Diagnostics.Debug.WriteLine ("Error : {0}", e);
}
}
return result;
}`
locator.PositionChanged += (sender, e) => {
var position = e.Position;
latitudeLabel.Text = position.Latitude;
longitudeLabel.Text = position.Longitude;
};
C- How to implement Geolocation in Xamarin.Android
C1: Location Services
Android offers access to various location technologies such as cell tower location, GPS, and Wi-Fi. The information of each location technology is fetched through location providers, therefore allowing applications to get the location irrespective of the provider used.
Let us here discuss the android Location Service API, and see how to communicate with the system location service a LocationManager. We will also explore the Google Location Services API using Fused Location Provider from Google Play Services which change location providers dynamically.
C2: Maps
Mobile devices easily complement mapping technologies which are an advantage over laptops and desktops as they do not have in-built location
awareness. Mobile devices use such location awareness applications for locating devices and for displaying a change in location information. Android holds a powerful in-built technology which displays location data on maps using location hardware which may be available on the device. Let us have a look what mapping applications under Xamarin.Android have to offer:
- In-built mapping functionality to quickly use in map application.
- Maps API to control a map’s display.
- A variety of techniques to add graphical overlays.
C3: How to get the location of a device using Android.Location.LocationManager
Let us know how to get the location of the device. Whenever a button on the activity is clicked by the user, the street address which is close the location will be displayed.
Note: Android applications should use the Fused Location Provider which is available through Google Play Services.
Steps Involved:
- First create a new Xamarin.Android application and name it as GetLocation.
- Now edit AssemblyInfo.cs, and declare the permissions necessary to use the LocationServices:
[assembly: UsesPermission(Manifest.Permission.AccessFineLocation)] [assembly: UsesPermission(Manifest.Permission.AccessCoarseLocation)
- Declare the permissions necessary to use the Geocoder class. Though it is not very much necessary for getting GPS coordinates of the device, but it is an attempt to give street address for current location:
[assembly: UsesPermission(Manifest.Permission.Internet)]
- Edit Main.axml so it contains two TextViews and a Button:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="20dp"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<TextView
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="wrap_content"
android:text="Location (when available)"
android:id="@+id/location_text"
android:layout_marginBottom="15dp" />
<Button
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="wrap_content"
android:id="@+id/get_address_button"
android:text="Get Address" />
<TextView
android:layout_width="fill_parent"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="wrap_content"
android:text="Address (when available)"
android:id="@+id/address_text"
android:layout_marginTop="10dp" />
</LinearLayout>
- Add some instance variables to Activity1.cs:
static readonly string TAG = "X:" + typeof (Activity1).Name;
TextView _addressText;
Location _currentLocation;
LocationManager _locationManager;
string _locationProvider;
TextView _locationText;
- Change OnCreate:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
_addressText = FindViewById<TextView>(Resource.Id.address_text);
_locationText = FindViewById<TextView>(Resource.Id.location_text);
FindViewById<TextView>(Resource.Id.get_address_button).Click += AddressButton_OnClick;
InitializeLocationManager();
}
Handler for button click will be covered below. The logic for initializing the LocationManager is placed in its own method for clarity.
- Add a method called InitializeLocationManager to Activity1:
void InitializeLocationManager()
{
_locationManager = (LocationManager) GetSystemService(LocationService);
Criteria criteriaForLocationService = new Criteria
{
Accuracy = Accuracy.Fine
};
IList<string> acceptableLocationProviders = _locationManager.GetProviders(criteriaForLocationService, true);
if (acceptableLocationProviders.Any())
{
_locationProvider = acceptableLocationProviders.First();
}
else
{
_locationProvider = string.Empty;
}
Log.Debug(TAG, "Using " + _locationProvider + ".");
}
The LocationManager class will listen for GPS updates from the device and notify the application by way of events. In this example we ask Android for the best location provider that matches a given set of Criteria and provide that provider to LocationManager.
- Edit Activity1.cs and have it implement the interface ILocationListener and add in the methods required by that interface:
[Activity(Label = "Get Location", MainLauncher = true, Icon = "@drawable/icon")]
public class Activity1 : Activity, ILocationListener
{
// removed code for clarity
public void OnLocationChanged(Location location) {}
public void OnProviderDisabled(string provider) {}
public void OnProviderEnabled(string provider) {}
public void OnStatusChanged(string provider, Availability status, Bundle extras) {}
}
- Override OnResume so that Activity1 will begin listening to the LocationManager when the activity comes into the foreground:
protected override void OnResume()
{
base.OnResume();
_locationManager.RequestLocationUpdates(_locationProvider, 0, 0, this);
}
- Override OnPause and unsubscribe Activity1 from the LocationManager when the activity goes into the background:
protected override void OnPause()
{
base.OnPause();
_locationManager.RemoveUpdates(this);
}
Note: By unsubscribing from the LocationManager we help in reducing the demands on the battery whenever the activity goes into the background.
- Add an event handler called AddressButton_OnClick to Activity1. This button allows the user to try and get the address from the latitude and longitude. The snippet below contains the code for AddressButton_OnClick:
async void AddressButton_OnClick(object sender, EventArgs eventArgs)
{
if (_currentLocation == null)
{
_addressText.Text = "Can't determine the current address. Try again in a few minutes.";
return;
}
Address address = await ReverseGeocodeCurrentLocation();
DisplayAddress(address);
}
async Task
ReverseGeocodeCurrentLocation()
{
Geocoder geocoder = new Geocoder(this);
IList
addressList =
await geocoder.GetFromLocationAsync(_currentLocation.Latitude, _currentLocation.Longitude, 10);
Address address = addressList.FirstOrDefault();
return address;
}
void DisplayAddress(Address address)
{
if (address != null)
{
StringBuilder deviceAddress = new StringBuilder();
for (int i = 0; i < address.MaxAddressLineIndex; i++)
{
deviceAddress.AppendLine(address.GetAddressLine(i));
}
// Remove the last comma from the end of the address.
_addressText.Text = deviceAddress.ToString();
}
else
{
_addressText.Text = "Unable to determine the address. Try again in a few minutes.";
}
}
The ReverseGeocodeCurrentLocation method will asynchronously lookup a collection of Address objects for the current location. There are many factors such as location and network availability, none, one or multiple addresses which will be returned. The first address will be passed to the method DisplayAddress which will display the address in the Activity.
- Update the method OnLocationChanged to display the latitude and longitude when GPS updates are received and update the address:
public async void OnLocationChanged(Location location)
{
_currentLocation = location;
if (_currentLocation == null)
{
_locationText.Text = "Unable to determine your location. Try again in a short while.";
}
else
{
_locationText.Text = string.Format("{0:f6},{1:f6}", _currentLocation.Latitude, _currentLocation.Longitude);
Address address = await ReverseGeocodeCurrentLocation();
DisplayAddress(address);
}
}
Add the System.Xml.Linq assembly, which can be done by Right clicking on Resources > Edit Resources and selecting the correct assembly.
- Now run the application. In a short while the location of the GPS gets be displayed. It may take a little while to display the coordinates: