using System; using System.Threading.Tasks; using System.Linq; using Xamarin.Forms; using Xamarin.Essentials; namespace InABox.Mobile { public class LocationServices { public delegate void LocationEvent(LocationServices sender); public event LocationEvent OnLocationFound; public delegate void LocationError(LocationServices sender, Exception error); public event LocationError OnLocationError; public TimeSpan ScanDelay { get; set; } public double Latitude { get; private set; } public double Longitude { get; private set; } public String Address { get; private set; } private bool bLocating = false; public DateTime TimeStamp { get; private set; } public LocationServices() : base() { TimeStamp = DateTime.MinValue; ScanDelay = new TimeSpan(0, 0, 0); Address = "Searching for GPS"; } public bool RecentlyLocated { get { return (DateTime.Now.Subtract(TimeStamp).Ticks < ScanDelay.Ticks); } } private double DistanceBetween(double sLatitude, double sLongitude, double eLatitude, double eLongitude) { var radiansOverDegrees = (Math.PI / 180.0); var sLatitudeRadians = sLatitude * radiansOverDegrees; var sLongitudeRadians = sLongitude * radiansOverDegrees; var eLatitudeRadians = eLatitude * radiansOverDegrees; var eLongitudeRadians = eLongitude * radiansOverDegrees; var dLongitude = eLongitudeRadians - sLongitudeRadians; var dLatitude = eLatitudeRadians - sLatitudeRadians; var result1 = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) + Math.Cos(sLatitudeRadians) * Math.Cos(eLatitudeRadians) * Math.Pow(Math.Sin(dLongitude / 2.0), 2.0); // Using 3956 as the number of miles around the earth var result2 = 3956.0 * 2.0 * Math.Atan2(Math.Sqrt(result1), Math.Sqrt(1.0 - result1)); return result2; } public void GetLocation(bool skiprecentlylocated = false) { if (bLocating || RecentlyLocated) { if (!skiprecentlylocated) return; } bLocating = true; // Don't reset this on every refresh, otherwise the final UI will randomly get "Searching for GPS" as the address //Address = "Searching for GPS"; bool bOK = MobileUtils.IsPermitted().Result; Task.Run(async () => { try { if (!bOK) { Latitude = 0.0F; Longitude = 0.0F; TimeStamp = DateTime.MinValue; Address = "GPS Services Disabled"; Device.BeginInvokeOnMainThread(() => { OnLocationError?.Invoke(this, new Exception("Please enable GPS Services to continue")); }); bOK = false; bLocating = false; } else { try { var request = new GeolocationRequest(GeolocationAccuracy.Best, new TimeSpan(0, 0, 20)); var location = await Geolocation.GetLocationAsync(request); if (location != null) { //if (location.IsFromMockProvider) //{ // Device.BeginInvokeOnMainThread(() => // { // OnLocationError?.Invoke(this, new Exception("Mock GPS Location Detected!\nPlease correct and restart TimeBench.")); // }); //} //else { Latitude = location.Latitude; Longitude = location.Longitude; TimeStamp = DateTime.Now; String sErr = ""; Placemark address = null; try { var addresses = await Geocoding.GetPlacemarksAsync(Latitude, Longitude); double maxdist = double.MaxValue; foreach (var cur in addresses.Where(x => !String.IsNullOrEmpty(x.Thoroughfare))) { var delta = Location.CalculateDistance(location, cur.Location, DistanceUnits.Kilometers); if (delta < maxdist) { address = cur; maxdist = delta; } } } catch (Exception ee2) { sErr = ee2.Message; //address = null; } if (address != null) Address = String.Format("{0} {1} {2}", address.SubThoroughfare, address.Thoroughfare, address.Locality); else Address = String.Format("Lat: {0}, Lng: {1}", Latitude, Longitude); if (location.IsFromMockProvider) Address = "** " + Address; if (!String.IsNullOrEmpty(sErr)) Address = String.Format("{0} (ERROR: {1})", Address, sErr); Device.BeginInvokeOnMainThread(() => { OnLocationFound?.Invoke(this); }); bLocating = false; } } else { Latitude = 0.00; Longitude = 0.00; TimeStamp = DateTime.MinValue; bLocating = false; Device.BeginInvokeOnMainThread(() => { OnLocationError?.Invoke(this, new Exception("Unable to get GPS Location")); }); } } catch (Exception e) { bLocating = false; Device.BeginInvokeOnMainThread(() => { OnLocationError?.Invoke(this, e); }); } } } catch (Exception e) { } bLocating = false; }); } } }