I almost always put all logic in som sort of separate classes in my projects. I simply think it looks better, is way more easy to understand and the better way.
When using Xamarin to create cross-plattform apps, you probably use dependency injection to minimize ugly code blocks like this.
if (Device.OS == TargetPlatform.iOS) {
}else{
}
That also means you pretty much need to create interfaces that you can use for all plattforms, which leads to this topic: You need to put logic in classes.
I had a bit of a struggle to implement location services on Android this way, hence this post. Most bloggs and articles i found used the main activity. For iOS it was very straight forward, so I’m leaving that part out.
And to be clear: This is my POC-code, not intended to copy and paste into production!
First of all, i had to create an interface i could use for iOS and Droid. I kept it simple, and only included stuff i needed: The current location, a method to initialize everything and a event to trigger when position changes.
public interface ILocationManager
{
Tuple<double, double> GetCurrentLocation();
void StartLocationUpdates();
event EventHandler LocationUpdated;
}
I also created i class holding the main activity context. I needed this to be able to use the context from my class.
public class Config {
public static MainActivity context = null;
}
In my MainActivity i set the context of the class above:
Config.context = this;
From my cross-plattform code i need to get a instance of ILocationManager for the current plattform. For this, i use the DependencyService. I Also call my StartLocationUpdates method to start getting location updates:
private ILocationManager LocManager = DependencyService.Get<ILocationManager> ();
LocManager.StartLocationUpdates ();
So far, all code except the config class is the same for both iOS and Android. But this is where i hit the wall on Android. I spent days trying to figure out how to create a class using my interface that would work in the same way my iOS code did. This is my solution
First, make sure to put this line above the namespace in your class to get dependency injection to work:
[assembly: Xamarin.Forms.Dependency (typeof(NautiCalc.Droid.LocationManager))]
The second thing i found important was to decorate your class with the Activity attribute. This makes sure your activity is inserted into the Manifest, which is important. You also need to inherit Activity class, and implement your interface.
[Activity]
public class LocationManager : Activity, NautiCalc.ILocationManager, ILocationListener
The rest is kind of straight forward. You need to implement a few methods because of the inheritance of Activity, and of course your own interface.
Here is my complete code:
using System;
using Android.Content;
using Android.App;
using Android.Locations;
using Android.OS;
using Android.Widget;
[assembly: Xamarin.Forms.Dependency (typeof(NautiCalc.Droid.LocationManager))]
namespace NautiCalc.Droid
{
[Activity]
public class LocationManager : Activity, NautiCalc.ILocationManager, ILocationListener
{
// event for the location changing
public event EventHandler LocationUpdated = delegate {};
protected Android.Locations.LocationManager locMgr;
public Android.Locations.LocationManager LocMgr {
get { return this.locMgr; }
}
public Tuple<double, double> GetCurrentLocation ()
{
Location lastKnown = locMgr.GetLastKnownLocation (Android.Locations.LocationManager.NetworkProvider);
if (lastKnown != null) {
return Tuple.Create (lastKnown.Latitude, lastKnown.Longitude);
}
return null;
}
public void StartLocationUpdates ()
{
locMgr = (Android.Locations.LocationManager)Config.context.GetSystemService (Context.LocationService);
if (locMgr.AllProviders.Contains (Android.Locations.LocationManager.GpsProvider) && locMgr.IsProviderEnabled (Android.Locations.LocationManager.GpsProvider)) {
locMgr.RequestLocationUpdates (Android.Locations.LocationManager.GpsProvider, 2000, 1, this);
}
}
protected override void OnPause ()
{
base.OnPause ();
locMgr.RemoveUpdates (this);
}
public void OnProviderEnabled (string provider)
{
}
public void OnProviderDisabled (string provider)
{
}
public void OnStatusChanged (string provider, Availability status, Bundle extras)
{
}
public void OnLocationChanged (Location location)
{
LocationData CurrentLocation = new LocationData ();
CurrentLocation.Altitude = location.Altitude;
CurrentLocation.Course = (double)location.Bearing;
CurrentLocation.Latitude = location.Latitude;
CurrentLocation.Longitude = location.Longitude;
CurrentLocation.Speed = (double)location.Speed;
LocationUpdated (this, new LocationUpdatedEventArgs (CurrentLocation));
}
}
}
Not that hard, once you figured it out. Happy coding!