public interface ILocationService { Coordinates GetLastSeenLocation(); Task GetCurrentLocation (); } public class LocationService : ILocationService { private const string Tag = "LocationService"; private readonly object _lock = new object(); private static readonly TimeSpan timeOut = TimeSpan.FromSeconds (10); private TaskCompletionSource taskSource; private readonly IMvxLocationWatcher locationWatcher; private readonly IMvxMainThreadDispatcher dispatcher; public LocationService (IMvxLocationWatcher locationWatcher, IMvxMainThreadDispatcher dispatcher) { this.locationWatcher = locationWatcher; this.dispatcher = dispatcher; this.locationWatcher.OnPermissionChanged += PermissionChanged; } public Coordinates GetLastSeenLocation() { var location = locationWatcher.LastSeenLocation; if (location == null) { return Coordinates.Empty; } return new Coordinates(location.Coordinates.Latitude, location.Coordinates.Longitude); } public Task GetCurrentLocation() { Task task; bool startWatcher = false; lock (_lock) { if (taskSource == null) { taskSource = new TaskCompletionSource(); startWatcher = true; } task = taskSource.Task; } if (startWatcher) { StartWatcher(); } return task; } private void StartWatcher () { Trace.Debug (Tag, "StartWatcher"); dispatcher.RequestMainThreadAction(() => { if (locationWatcher.Started) { Trace.Warn (Tag, "Watcher already started!"); return; } locationWatcher.Start(new MvxLocationOptions() { Accuracy = MvxLocationAccuracy.Coarse }, location => { locationWatcher.Stop(); OnSuccess (new Coordinates(location.Coordinates.Latitude, location.Coordinates.Longitude)); }, error => { locationWatcher.Stop(); OnError (new LocationException(error.Code)); }); Task.Delay (timeOut).ContinueWith (_ => OnTimeout ()); }); } private void OnTimeout () { Trace.Debug(Tag, "Timeout"); TaskCompletionSource source; lock (_lock) { source = taskSource; taskSource = null; } source?.SetException (new LocationException (MvxLocationErrorCode.Timeout)); if (locationWatcher.Started) { locationWatcher.Stop(); } } private void OnSuccess (Coordinates coordinates) { Trace.Debug(Tag, "Updated: " + coordinates); TaskCompletionSource source; lock (_lock) { source = taskSource; taskSource = null; } source?.SetResult(coordinates); } private void OnError (LocationException error) { Trace.Warn(Tag, "Error: " + error.Code.ToString()); TaskCompletionSource source; lock (_lock) { source = taskSource; taskSource = null; } source?.SetException(error); } private void PermissionChanged (object sender, MvxValueEventArgs e) { Trace.Warn(Tag, "Permission changed: " + e.Value.ToString ()); if (e.Value != MvxLocationPermission.Denied) { return; } // Process only if denied TaskCompletionSource source; lock (_lock) { source = taskSource; taskSource = null; } source?.SetException(new LocationException (MvxLocationErrorCode.PermissionDenied)); } }