From b9835a3e721253d7897ea019aea08e393cc7d24b Mon Sep 17 00:00:00 2001 From: David Britch Date: Tue, 20 Feb 2018 17:52:19 +0000 Subject: [PATCH] iOS LocationService complete. --- .../eShopOnContainers.Core/App.xaml.cs | 5 +- .../Services/Basket/BasketMockService.cs | 12 ++-- .../Services/Catalog/CatalogMockService.cs | 20 +++---- .../Services/Marketing/CampaignMockService.cs | 4 +- .../Services/Order/OrderMockService.cs | 6 +- .../Services/User/UserMockService.cs | 2 +- .../ViewModels/LoginViewModel.cs | 4 +- .../GeolocationSingleUpdateDelegate.cs | 34 +---------- .../Services/LocationServiceImplementation.cs | 57 +------------------ .../Services/PermissionsService.cs | 17 ------ 10 files changed, 31 insertions(+), 130 deletions(-) diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs index 2d7322d14..cc8b4cc58 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/App.xaml.cs @@ -48,12 +48,10 @@ namespace eShopOnContainers { await InitNavigation(); } - if (_settingsService.AllowGpsLocation && !_settingsService.UseFakeLocation) { await GetGpsLocation(); } - if (!_settingsService.UseMocks && !string.IsNullOrEmpty(_settingsService.AuthAccessToken)) { await SendCurrentLocation(); @@ -74,9 +72,10 @@ namespace eShopOnContainers if (locator.IsGeolocationEnabled && locator.IsGeolocationAvailable) { - //locationService.AllowsBackgroundUpdates = true; locator.DesiredAccuracy = 50; + // Delay getting the position to ensure that the UI has finished updating + await Task.Delay(2000); var position = await locator.GetPositionAsync(); _settingsService.Latitude = position.Latitude.ToString(); diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketMockService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketMockService.cs index 60a13af1e..8c54a8993 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketMockService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Basket/BasketMockService.cs @@ -13,16 +13,16 @@ namespace eShopOnContainers.Core.Services.Basket BuyerId = "9245fe4a-d402-451c-b9ed-9c1a04247482", Items = new List { - new BasketItem { Id = "1", PictureUrl = Device.RuntimePlatform != Device.UWP ? "fake_product_01.png" : "Assets/fake_product_01.png", ProductId = Common.Common.MockCatalogItemId01, ProductName = ".NET Bot Blue Sweatshirt (M)", Quantity = 1, UnitPrice = 19.50M }, - new BasketItem { Id = "2", PictureUrl = Device.RuntimePlatform != Device.UWP ? "fake_product_04.png" : "Assets/fake_product_04.png", ProductId = Common.Common.MockCatalogItemId04, ProductName = ".NET Black Cupt", Quantity = 1, UnitPrice = 17.00M } + new BasketItem { Id = "1", PictureUrl = Device.RuntimePlatform != Device.UWP ? "fake_product_01.png" : "Assets/fake_product_01.png", ProductId = Common.Common.MockCatalogItemId01, ProductName = ".NET Bot Blue Sweatshirt (M)", Quantity = 1, UnitPrice = 19.50M }, + new BasketItem { Id = "2", PictureUrl = Device.RuntimePlatform != Device.UWP ? "fake_product_04.png" : "Assets/fake_product_04.png", ProductId = Common.Common.MockCatalogItemId04, ProductName = ".NET Black Cupt", Quantity = 1, UnitPrice = 17.00M } } }; public async Task GetBasketAsync(string guidUser, string token) { - await Task.Delay(500); + await Task.Delay(10); - if(string.IsNullOrEmpty(guidUser) || string.IsNullOrEmpty(token)) + if (string.IsNullOrEmpty(guidUser) || string.IsNullOrEmpty(token)) { return new CustomerBasket(); } @@ -32,7 +32,7 @@ namespace eShopOnContainers.Core.Services.Basket public async Task UpdateBasketAsync(CustomerBasket customerBasket, string token) { - await Task.Delay(500); + await Task.Delay(10); if (string.IsNullOrEmpty(token)) { @@ -46,7 +46,7 @@ namespace eShopOnContainers.Core.Services.Basket public async Task ClearBasketAsync(string guidUser, string token) { - await Task.Delay(500); + await Task.Delay(10); if (string.IsNullOrEmpty(token)) { diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogMockService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogMockService.cs index 213b083f2..db6aee837 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogMockService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Catalog/CatalogMockService.cs @@ -24,40 +24,40 @@ namespace eShopOnContainers.Core.Services.Catalog private ObservableCollection MockCatalog = new ObservableCollection { - new CatalogItem { Id = Common.Common.MockCatalogItemId01, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_01.png" : "Assets/fake_product_01.png", Name = ".NET Bot Blue Sweatshirt (M)", Price = 19.50M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 2, CatalogType = "T-Shirt" }, - new CatalogItem { Id = Common.Common.MockCatalogItemId02, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_02.png" : "Assets/fake_product_02.png", Name = ".NET Bot Purple Sweatshirt (M)", Price = 19.50M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 2, CatalogType = "T-Shirt" }, - new CatalogItem { Id = Common.Common.MockCatalogItemId03, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_03.png" : "Assets/fake_product_03.png", Name = ".NET Bot Black Sweatshirt (M)", Price = 19.95M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 2, CatalogType = "T-Shirt" }, - new CatalogItem { Id = Common.Common.MockCatalogItemId04, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_04.png" : "Assets/fake_product_04.png", Name = ".NET Black Cupt", Price = 17.00M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 1, CatalogType = "Mug" }, - new CatalogItem { Id = Common.Common.MockCatalogItemId05, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_05.png" : "Assets/fake_product_05.png", Name = "Azure Black Sweatshirt (M)", Price = 19.50M, CatalogBrandId = 1, CatalogBrand = "Azure", CatalogTypeId = 2, CatalogType = "T-Shirt" } + new CatalogItem { Id = Common.Common.MockCatalogItemId01, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_01.png" : "Assets/fake_product_01.png", Name = ".NET Bot Blue Sweatshirt (M)", Price = 19.50M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 2, CatalogType = "T-Shirt" }, + new CatalogItem { Id = Common.Common.MockCatalogItemId02, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_02.png" : "Assets/fake_product_02.png", Name = ".NET Bot Purple Sweatshirt (M)", Price = 19.50M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 2, CatalogType = "T-Shirt" }, + new CatalogItem { Id = Common.Common.MockCatalogItemId03, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_03.png" : "Assets/fake_product_03.png", Name = ".NET Bot Black Sweatshirt (M)", Price = 19.95M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 2, CatalogType = "T-Shirt" }, + new CatalogItem { Id = Common.Common.MockCatalogItemId04, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_04.png" : "Assets/fake_product_04.png", Name = ".NET Black Cupt", Price = 17.00M, CatalogBrandId = 2, CatalogBrand = "Visual Studio", CatalogTypeId = 1, CatalogType = "Mug" }, + new CatalogItem { Id = Common.Common.MockCatalogItemId05, PictureUri = Device.RuntimePlatform != Device.UWP ? "fake_product_05.png" : "Assets/fake_product_05.png", Name = "Azure Black Sweatshirt (M)", Price = 19.50M, CatalogBrandId = 1, CatalogBrand = "Azure", CatalogTypeId = 2, CatalogType = "T-Shirt" } }; public async Task> GetCatalogAsync() { - await Task.Delay(500); + await Task.Delay(10); return MockCatalog; } public async Task> FilterAsync(int catalogBrandId, int catalogTypeId) { - await Task.Delay(500); + await Task.Delay(10); return MockCatalog .Where(c => c.CatalogBrandId == catalogBrandId && - c.CatalogTypeId == catalogTypeId) + c.CatalogTypeId == catalogTypeId) .ToObservableCollection(); } public async Task> GetCatalogBrandAsync() { - await Task.Delay(500); + await Task.Delay(10); return MockCatalogBrand; } public async Task> GetCatalogTypeAsync() { - await Task.Delay(500); + await Task.Delay(10); return MockCatalogType; } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignMockService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignMockService.cs index e24405b53..fdc7c75fd 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignMockService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Marketing/CampaignMockService.cs @@ -38,13 +38,13 @@ namespace eShopOnContainers.Core.Services.Marketing public async Task> GetAllCampaignsAsync(string token) { - await Task.Delay(500); + await Task.Delay(10); return _mockCampaign; } public async Task GetCampaignByIdAsync(int campaignId, string token) { - await Task.Delay(500); + await Task.Delay(10); return _mockCampaign.SingleOrDefault(c => c.Id == campaignId); } } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs index 5fdc7c281..185989255 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/Order/OrderMockService.cs @@ -74,7 +74,7 @@ namespace eShopOnContainers.Core.Services.Order public async Task> GetOrdersAsync(string token) { - await Task.Delay(500); + await Task.Delay(10); if (!string.IsNullOrEmpty(token)) { @@ -88,7 +88,7 @@ namespace eShopOnContainers.Core.Services.Order public async Task GetOrderAsync(int orderId, string token) { - await Task.Delay(500); + await Task.Delay(10); if (!string.IsNullOrEmpty(token)) return MockOrders @@ -99,7 +99,7 @@ namespace eShopOnContainers.Core.Services.Order public async Task CreateOrderAsync(Models.Orders.Order newOrder, string token) { - await Task.Delay(500); + await Task.Delay(10); if (!string.IsNullOrEmpty(token)) { diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/User/UserMockService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/User/UserMockService.cs index 5b32bc296..fb75ec03e 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/User/UserMockService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/Services/User/UserMockService.cs @@ -28,7 +28,7 @@ namespace eShopOnContainers.Core.Services.User public async Task GetUserInfoAsync(string authToken) { - await Task.Delay(500); + await Task.Delay(10); return MockUserInfo; } } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs index 02711fb39..2ed8cf83c 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.Core/ViewModels/LoginViewModel.cs @@ -160,7 +160,7 @@ namespace eShopOnContainers.Core.ViewModels { try { - await Task.Delay(1000); + await Task.Delay(10); isAuthenticated = true; } @@ -189,7 +189,7 @@ namespace eShopOnContainers.Core.ViewModels { IsBusy = true; - await Task.Delay(500); + await Task.Delay(10); LoginUrl = _identityService.CreateAuthorizationRequest(); diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/GeolocationSingleUpdateDelegate.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/GeolocationSingleUpdateDelegate.cs index 110ac1eff..980aa8a55 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/GeolocationSingleUpdateDelegate.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/GeolocationSingleUpdateDelegate.cs @@ -10,24 +10,19 @@ namespace eShopOnContainers.iOS.Services { internal class GeolocationSingleUpdateDelegate : CLLocationManagerDelegate { - bool _haveHeading; bool _haveLocation; readonly Position _position = new Position(); - CLHeading _bestHeading; - readonly double _desiredAccuracy; - readonly bool _includeHeading; readonly TaskCompletionSource _tcs; readonly CLLocationManager _manager; public Task Task => _tcs?.Task; - public GeolocationSingleUpdateDelegate(CLLocationManager manager, double desiredAccuracy, bool includeHeading, int timeout, CancellationToken cancelToken) + public GeolocationSingleUpdateDelegate(CLLocationManager manager, double desiredAccuracy, int timeout, CancellationToken cancelToken) { _manager = manager; _tcs = new TaskCompletionSource(manager); _desiredAccuracy = desiredAccuracy; - _includeHeading = includeHeading; if (timeout != Timeout.Infinite) { @@ -76,8 +71,6 @@ namespace eShopOnContainers.iOS.Services } } - public override bool ShouldDisplayHeadingCalibration(CLLocationManager manager) => true; - public override void UpdatedLocation(CLLocationManager manager, CLLocation newLocation, CLLocation oldLocation) { if (newLocation.HorizontalAccuracy < 0) @@ -97,31 +90,13 @@ namespace eShopOnContainers.iOS.Services { _position.Timestamp = new DateTimeOffset((DateTime)newLocation.Timestamp); } - catch (Exception ex) + catch (Exception) { _position.Timestamp = DateTimeOffset.UtcNow; } _haveLocation = true; - if ((!_includeHeading || _haveHeading) && _position.Accuracy <= _desiredAccuracy) - { - _tcs.TrySetResult(new Position(_position)); - StopListening(); - } - } - - public override void UpdatedHeading(CLLocationManager manager, CLHeading newHeading) - { - if (newHeading.HeadingAccuracy < 0) - return; - if (_bestHeading != null && newHeading.HeadingAccuracy >= _bestHeading.HeadingAccuracy) - return; - - _bestHeading = newHeading; - _position.Heading = newHeading.TrueHeading; - _haveHeading = true; - - if (_haveLocation && _position.Accuracy <= _desiredAccuracy) + if (_position.Accuracy <= _desiredAccuracy) { _tcs.TrySetResult(new Position(_position)); StopListening(); @@ -130,9 +105,6 @@ namespace eShopOnContainers.iOS.Services private void StopListening() { - if (CLLocationManager.HeadingAvailable) - _manager.StopUpdatingHeading(); - _manager.StopUpdatingLocation(); } } diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/LocationServiceImplementation.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/LocationServiceImplementation.cs index 12bef0775..6e77dc7c0 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/LocationServiceImplementation.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/LocationServiceImplementation.cs @@ -15,12 +15,6 @@ namespace eShopOnContainers.iOS.Services { public class LocationServiceImplementation : ILocationServiceImplementation { - bool _deferringUpdates; - readonly CLLocationManager _manager; - Position _lastPosition; - - public event EventHandler PositionError; - public event EventHandler PositionChanged; public double DesiredAccuracy { get; set; } public bool IsGeolocationAvailable => true; public bool IsGeolocationEnabled @@ -32,21 +26,11 @@ namespace eShopOnContainers.iOS.Services } } - public bool SupportsHeading => CLLocationManager.HeadingAvailable; - public LocationServiceImplementation() { DesiredAccuracy = 100; - //_manager = GetManager(); - //_manager.AuthorizationChanged += OnAuthorizationChanged; - //_manager.Failed += OnFailed; - //_manager.UpdatedLocation += OnUpdatedLocation; - //_manager.UpdatedHeading += OnUpdatedHeading; - //_manager.DeferredUpdatesFinished += OnDeferredUpdatesFinished; } - void OnDeferredUpdatesFinished(object sender, NSErrorEventArgs e) => _deferringUpdates = false; - #region Internal Implementation async Task CheckPermissions(Permission permission) @@ -78,7 +62,7 @@ namespace eShopOnContainers.iOS.Services #region ILocationServiceImplementation - public async Task GetPositionAsync(TimeSpan? timeout, CancellationToken? cancelToken = null, bool includeHeading = false) + public async Task GetPositionAsync(TimeSpan? timeout, CancellationToken? cancelToken = null) { var permission = Permission.LocationWhenInUse; var hasPermission = await CheckPermissions(permission); @@ -96,53 +80,16 @@ namespace eShopOnContainers.iOS.Services var manager = GetManager(); manager.DesiredAccuracy = DesiredAccuracy; - // Permit background updates if background location mode is enabled - if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0)) - { - var backgroundModes = NSBundle.MainBundle.InfoDictionary[(NSString)"UIBackgroundModes"] as NSArray; - manager.AllowsBackgroundLocationUpdates = backgroundModes != null && (backgroundModes.Contains((NSString)"Location") || backgroundModes.Contains((NSString)"location")); - } - // Always prevent location update pausing since we're only listening for a single update if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0)) manager.PausesLocationUpdatesAutomatically = false; tcs = new TaskCompletionSource(manager); - var singleListener = new GeolocationSingleUpdateDelegate(manager, DesiredAccuracy, includeHeading, timeoutMilliseconds, cancelToken.Value); + var singleListener = new GeolocationSingleUpdateDelegate(manager, DesiredAccuracy, timeoutMilliseconds, cancelToken.Value); manager.Delegate = singleListener; manager.StartUpdatingLocation(); - if (includeHeading && SupportsHeading) - manager.StartUpdatingHeading(); - return await singleListener.Task; - - //tcs = new TaskCompletionSource(); - //if (_lastPosition == null) - //{ - // if (cancelToken != CancellationToken.None) - // cancelToken.Value.Register(() => tcs.TrySetCanceled()); - - // EventHandler gotError = null; - // gotError = (s, e) => - // { - // tcs.TrySetException(new GeolocationException(e.Error)); - // PositionError -= gotError; - // }; - // PositionError += gotError; - - // EventHandler gotPosition = null; - // gotPosition = (s, e) => - // { - // tcs.TrySetResult(e.Position); - // PositionChanged += gotPosition; - // }; - // PositionChanged += gotPosition; - //} - //else - // tcs.SetResult(_lastPosition); - - //return await tcs.Task; } #endregion diff --git a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/PermissionsService.cs b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/PermissionsService.cs index 6f87b60dc..8d21757dc 100644 --- a/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/PermissionsService.cs +++ b/src/Mobile/eShopOnContainers/eShopOnContainers.iOS/Services/PermissionsService.cs @@ -19,7 +19,6 @@ namespace eShopOnContainers.iOS.Services return PermissionStatus.Disabled; var status = CLLocationManager.Status; - if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) { switch (status) @@ -77,22 +76,6 @@ namespace eShopOnContainers.iOS.Services locationManager.AuthorizationChanged += authCallback; var info = NSBundle.MainBundle.InfoDictionary; - //if (permission == Permission.Location) - //{ - // if (info.ContainsKey(new NSString("NSLocationAlwaysUsageDescription"))) - // locationManager.RequestAlwaysAuthorization(); - // else if (info.ContainsKey(new NSString("NSLocationWhenInUseUsageDescription"))) - // locationManager.RequestWhenInUseAuthorization(); - // else - // throw new UnauthorizedAccessException("On iOS 8.0 and higher you must set either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in your Info.plist file to enable Authorization Requests for Location updates!"); - //} - //else if (permission == Permission.LocationAlways) - //{ - // if (info.ContainsKey(new NSString("NSLocationAlwaysUsageDescription"))) - // locationManager.RequestAlwaysAuthorization(); - // else - // throw new UnauthorizedAccessException("On iOS 8.0 and higher you must set either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in your Info.plist file to enable Authorization Requests for Location updates!"); - //} if (permission == Permission.LocationWhenInUse) { if (info.ContainsKey(new NSString("NSLocationWhenInUseUsageDescription")))