diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Helpers/Timeout.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Helpers/Timeout.cs new file mode 100644 index 000000000..7306a3c78 --- /dev/null +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Helpers/Timeout.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace eShopOnContainers.Windows.Helpers +{ + internal class Timeout + { + public const int Infinite = -1; + readonly CancellationTokenSource _canceller = new CancellationTokenSource(); + + public Timeout(int timeout, Action timesUp) + { + if (timeout == Infinite) + return; + if (timeout < 0) + throw new ArgumentOutOfRangeException("timeoutMilliseconds"); + if (timesUp == null) + throw new ArgumentNullException("timesUp"); + + Task.Delay(TimeSpan.FromMilliseconds(timeout), _canceller.Token).ContinueWith(t => + { + if (!t.IsCanceled) + timesUp(); + }); + } + + public void Cancel() + { + _canceller.Cancel(); + } + } +} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Services/LocationServiceImplementation.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Services/LocationServiceImplementation.cs new file mode 100644 index 000000000..27749744a --- /dev/null +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/Services/LocationServiceImplementation.cs @@ -0,0 +1,166 @@ +using eShopOnContainers.Core.Models.Location; +using eShopOnContainers.Core.Services.Location; +using System; +using System.Threading; +using System.Threading.Tasks; +using Windows.Devices.Geolocation; +using Windows.Foundation; + +[assembly: Xamarin.Forms.Dependency(typeof(ILocationServiceImplementation))] +namespace eShopOnContainers.Windows.Services +{ + public class LocationServiceImplementation : ILocationServiceImplementation + { + double _desiredAccuracy; + Geolocator _locator = new Geolocator(); + + public LocationServiceImplementation() + { + DesiredAccuracy = 100; + } + + #region Internal Implementation + + Geolocator GetGeolocator() + { + var loc = _locator; + if (loc == null) + { + _locator = new Geolocator(); + _locator.StatusChanged += OnLocatorStatusChanged; + loc = _locator; + } + return loc; + } + + PositionStatus GetGeolocatorStatus() + { + var loc = GetGeolocator(); + return loc.LocationStatus; + } + + static Position GetPosition(Geoposition position) + { + var pos = new Position + { + Accuracy = position.Coordinate.Accuracy, + Altitude = position.Coordinate.Point.Position.Altitude, + Latitude = position.Coordinate.Point.Position.Latitude, + Longitude = position.Coordinate.Point.Position.Longitude, + Timestamp = position.Coordinate.Timestamp.ToUniversalTime() + }; + + if (position.Coordinate.Heading != null) + pos.Heading = position.Coordinate.Heading.Value; + if (position.Coordinate.Speed != null) + pos.Speed = position.Coordinate.Speed.Value; + if (position.Coordinate.AltitudeAccuracy.HasValue) + pos.AltitudeAccuracy = position.Coordinate.AltitudeAccuracy.Value; + + return pos; + } + + async void OnLocatorStatusChanged(Geolocator sender, StatusChangedEventArgs e) + { + GeolocationError error; + + switch (e.Status) + { + case PositionStatus.Disabled: + error = GeolocationError.Unauthorized; + break; + case PositionStatus.NoData: + error = GeolocationError.PositionUnavailable; + break; + default: + return; + } + _locator = null; + } + + #endregion + + #region ILocationServiceImplementation + + public double DesiredAccuracy + { + get { return _desiredAccuracy; } + set + { + _desiredAccuracy = value; + GetGeolocator().DesiredAccuracy = (value < 100) ? PositionAccuracy.High : PositionAccuracy.Default; + } + } + + public event EventHandler PositionError; + public event EventHandler PositionChanged; + + public bool IsGeolocationAvailable + { + get + { + var status = GetGeolocatorStatus(); + while (status == PositionStatus.Initializing) + { + Task.Delay(10).Wait(); + status = GetGeolocatorStatus(); + } + return status != PositionStatus.NotAvailable; + } + } + + public bool IsGeolocationEnabled + { + get + { + var status = GetGeolocatorStatus(); + while (status == PositionStatus.Initializing) + { + Task.Delay(10).Wait(); + status = GetGeolocatorStatus(); + } + return status != PositionStatus.Disabled && status != PositionStatus.NotAvailable; + } + } + + public Task GetPositionAsync(TimeSpan? timeout = null, CancellationToken? cancelToken = null, bool includeHeading = false) + { + var timeoutMilliseconds = timeout.HasValue ? (int)timeout.Value.TotalMilliseconds : eShopOnContainers.Windows.Helpers.Timeout.Infinite; + if (timeoutMilliseconds < 0 && timeoutMilliseconds != eShopOnContainers.Windows.Helpers.Timeout.Infinite) + throw new ArgumentOutOfRangeException(nameof(timeout)); + + if (!cancelToken.HasValue) + cancelToken = CancellationToken.None; + + var pos = GetGeolocator().GetGeopositionAsync(TimeSpan.FromTicks(0), TimeSpan.FromDays(365)); + cancelToken.Value.Register(o => ((IAsyncOperation)o).Cancel(), pos); + var timer = new eShopOnContainers.Windows.Helpers.Timeout(timeoutMilliseconds, pos.Cancel); + var tcs = new TaskCompletionSource(); + + pos.Completed = (op, s) => + { + timer.Cancel(); + + switch (s) + { + case AsyncStatus.Canceled: + tcs.SetCanceled(); + break; + case AsyncStatus.Completed: + tcs.SetResult(GetPosition(op.GetResults())); + break; + case AsyncStatus.Error: + var ex = op.ErrorCode; + if (ex is UnauthorizedAccessException) + ex = new GeolocationException(GeolocationError.Unauthorized, ex); + + tcs.SetException(ex); + break; + } + }; + return tcs.Task; + } + + #endregion + } +} diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj index a1ca59d2f..b879d31c6 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Windows/eShopOnContainers.Windows.csproj @@ -115,12 +115,14 @@ + MainPage.xaml +