This short post is about the easiest way to get GPS Location Tracking with your Android Phone, Xamarin and C#.
Imagine you have a navigation/tracking application and you need to get the GPS position of the device with real-time accuracy (and not periodically, each 5-15 minutes, like Google Location History does).
In this case you may find useful the following class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Locations;
namespace LocationTrackerTest
{
/// <summary>
/// Class provides GPS location.
/// Note: Requires permission ACCESS_FINE_LOCATION.
/// Usage:
/// 1. Create the class instance.
/// 2. Call InitializeLocationManager().
/// 3. Check "CurrentLocation" when you wish or
/// subscribe to "LocationChanged" event to be notified.
/// 4. When you do not need it any more - call Dispose().
/// </summary>
public class GPSLocationTracker : Java.Lang.Object, ILocationListener, IDisposable
{
/// <summary>
/// Current location
/// </summary>
public Location CurrentLocation { get; set; }
/// <summary>
/// Current location (if available) or status message if not available
/// </summary>
public string CurrentLocationString { get; set; }
public Availability CurrentGPSProviderStatus = Availability.Available;
/// <summary>
/// Tracker Status - true if ok, false if no GPS location
/// </summary>
public bool IsGettingLocation { get; set; }
/// <summary>
/// True if GPS provider is enabled in settings, otherwise false
/// </summary>
public bool IsGPSProviderEnabled { get; set; }
// Event handlers
public event EventHandler<Location> LocationChanged;
public event EventHandler GPSProviderDisabled;
public event EventHandler GPSProviderEnabled;
public event EventHandler<Availability> GPSStatusChanged;
public LocationManager _locationManager = null;
Context _context;
bool _isGiveToastsOnStatusChanges;
bool _isFirstLocationReported = false;
// The minimum distance to change Updates in meters
long _minDistanceChangeForUpdatesMeters = 10; // 10 meters
// The minimum time between updates in milliseconds
long _minTimeBetweenUpdatesMs = 1000 * 30; // 30 seconds
public GPSLocationTracker(Context context, bool giveToastsOnStatusChanges = true, long minDistanceChangeForUpdatesMeters = 10, long minTimeBetweenUpdatesMs = 30000)
{
this._context = context;
this._isGiveToastsOnStatusChanges = giveToastsOnStatusChanges;
_minDistanceChangeForUpdatesMeters = minDistanceChangeForUpdatesMeters;
_minTimeBetweenUpdatesMs = minTimeBetweenUpdatesMs;
IsGettingLocation = false;
IsGPSProviderEnabled = false;
}
/// <summary>
/// Initialize location services and request getting location updates and events.
/// Run this methid after construction of the class instance.
/// Run it in the UI thread if you wish the GPS settings alert to be automatically displayed.
/// </summary>
public void InitializeLocationManager(bool isShowGPSSettingsAlert = true)
{
// Get location manager
_locationManager = (LocationManager)_context.GetSystemService(Context.LocationService);
// Check if GPS is enabled, If not - display alert with shortcut to GPS settings
IsGPSProviderEnabled = _locationManager.IsProviderEnabled(LocationManager.GpsProvider);
if (IsGPSProviderEnabled)
CurrentGPSProviderStatus = Availability.Available;
else
CurrentGPSProviderStatus = Availability.OutOfService;
if (!IsGPSProviderEnabled && isShowGPSSettingsAlert)
ShowGPSSettingsAlert();
// Subscibe to getting location updates with the desired treshold
_locationManager.RequestLocationUpdates(LocationManager.GpsProvider, _minTimeBetweenUpdatesMs, _minDistanceChangeForUpdatesMeters, this);
CurrentLocation = _locationManager.GetLastKnownLocation(LocationManager.GpsProvider);
}
/// <summary>
/// Display GPS disabled alert with shortcut to GPS settings
/// </summary>
public void ShowGPSSettingsAlert()
{
// See http://stacktips.com/tutorials/xamarin/alertdialog-and-dialogfragment-example-in-xamarin-android
AlertDialog.Builder alertDialog = new AlertDialog.Builder(_context);
// Setting Dialog Title
alertDialog.SetTitle("GPS settings");
// Setting Dialog Message
alertDialog.SetMessage("GPS is not enabled. Do you want to go to settings menu?");
// On pressing Settings button
alertDialog.SetPositiveButton("Settings", (senderAlert, args) => {
Intent intent = new Intent(Android.Provider.Settings.ActionLocationSourceSettings);
_context.StartActivity(intent);
});
// On pressing cancel button
alertDialog.SetNegativeButton("Cancel", (senderAlert, args) => { });
// Showing Alert Message (note - do it only on UI thread, or use Activity.RunOnUiThread method)
Dialog dialog = alertDialog.Create();
dialog.Show();
}
/// <summary>
/// LocationListener will call this method when updates come from LocationManager
/// </summary>
/// <param name="location"></param>
public void OnLocationChanged(Location location)
{
if (location == null)
{
CurrentLocationString = "No location";
_isFirstLocationReported = false;
IsGettingLocation = false;
if (_isFirstLocationReported && _isGiveToastsOnStatusChanges)
{ // if after we had location, we lost it and got null
Toast.MakeText(_context, String.Format("Location update: null", CurrentLocation.Latitude, CurrentLocation.Longitude), ToastLength.Short).Show();
}
}
else
{
CurrentLocation = location;
IsGettingLocation = true;
CurrentLocationString = String.Format("{0}:{1}", CurrentLocation.Latitude, CurrentLocation.Longitude);
if (!_isFirstLocationReported && _isGiveToastsOnStatusChanges)
{
Toast.MakeText(_context, String.Format("Location update: {0}:{1}", CurrentLocation.Latitude, CurrentLocation.Longitude), ToastLength.Short).Show();
_isFirstLocationReported = true;
}
}
// Fire event
if (LocationChanged != null)
LocationChanged(this, location);
}
public void OnProviderDisabled(string provider)
{
if (provider == LocationManager.GpsProvider)
{
if (_isGiveToastsOnStatusChanges)
Toast.MakeText(_context, "GPS provider disabled.", ToastLength.Short).Show();
CurrentLocationString = "GPS provider disabled.";
IsGettingLocation = false;
_isFirstLocationReported = false;
IsGPSProviderEnabled = false;
// Fire event
if (GPSProviderDisabled != null)
GPSProviderDisabled(this, null);
}
}
public void OnProviderEnabled(string provider)
{
if (provider == LocationManager.GpsProvider)
{
if (_isGiveToastsOnStatusChanges)
Toast.MakeText(_context, "GPS provider enabled", ToastLength.Short).Show();
CurrentLocationString = "GPS provider enabled.";
_isFirstLocationReported = false;
IsGPSProviderEnabled = false;
// Fire event
if (GPSProviderEnabled != null)
GPSProviderEnabled(this, null);
}
}
public void OnStatusChanged(string provider, Availability status, Bundle extras)
{
if (provider == LocationManager.GpsProvider)
{
if (CurrentGPSProviderStatus != status)
{
if (_isGiveToastsOnStatusChanges)
Toast.MakeText(_context, "GPS Status changed: " + status.ToString(), ToastLength.Short).Show();
CurrentLocationString = "GPS Status " + status.ToString();
CurrentGPSProviderStatus = status;
if (status != Availability.Available)
{
IsGettingLocation = false;
_isFirstLocationReported = false;
}
// Fire event
if (GPSStatusChanged != null)
GPSStatusChanged(this, status);
}
}
}
/// <summary>
/// Stop location services and unsubscribe from getting events
/// </summary>
public void StopLocationManager()
{
if (_locationManager != null)
{
_locationManager.RemoveUpdates(this);
}
}
public void Dispose()
{
StopLocationManager();
}
}
}
This class initializes the LocationManager, Checks if the GPS data provider is enabled (if not, it optionally displays a message box with shortcut to GPS settings), and starts listening to location updates with given threshold in meters and timeout.
To illustrate the usage of this class, I’ve created a sample and simple application which displays the GPS position from GPSLocationTracker class on the screen:
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
namespace LocationTrackerTest
{
[Activity(Label = "LocationTrackerTest", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
GPSLocationTracker gpsLocationTracker = null;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Create and initialize the GPSLocationTracker,
// subscribe to location changed event
gpsLocationTracker = new GPSLocationTracker(this, true);
gpsLocationTracker.InitializeLocationManager(true);
gpsLocationTracker.LocationChanged += GpsLocationTracker_LocationChanged;
}
/// <summary>
/// gpsLocationTracker.LocationChanged event handler
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void GpsLocationTracker_LocationChanged(object sender, Android.Locations.Location e)
{
// Just show current location in a text field
TextView tv = FindViewById<TextView>(Resource.Id.txtLocation);
tv.Text = gpsLocationTracker.CurrentLocationString;
}
}
}
There are just 3 lines of code required to initialize this class and subscribe to LocationChanged event. Then you need to create your LocationChanged handler and there perform all the actions you need – like update the position on the map, send the device location update to the server etc.
Note that you should add ACCESS_FINE_LOCATION permission to your application manifest: