iOS LocationService complete.

This commit is contained in:
David Britch 2018-02-20 17:52:19 +00:00
parent 1fa48de667
commit b9835a3e72
10 changed files with 31 additions and 130 deletions

View File

@ -48,12 +48,10 @@ namespace eShopOnContainers
{ {
await InitNavigation(); await InitNavigation();
} }
if (_settingsService.AllowGpsLocation && !_settingsService.UseFakeLocation) if (_settingsService.AllowGpsLocation && !_settingsService.UseFakeLocation)
{ {
await GetGpsLocation(); await GetGpsLocation();
} }
if (!_settingsService.UseMocks && !string.IsNullOrEmpty(_settingsService.AuthAccessToken)) if (!_settingsService.UseMocks && !string.IsNullOrEmpty(_settingsService.AuthAccessToken))
{ {
await SendCurrentLocation(); await SendCurrentLocation();
@ -74,9 +72,10 @@ namespace eShopOnContainers
if (locator.IsGeolocationEnabled && locator.IsGeolocationAvailable) if (locator.IsGeolocationEnabled && locator.IsGeolocationAvailable)
{ {
//locationService.AllowsBackgroundUpdates = true;
locator.DesiredAccuracy = 50; locator.DesiredAccuracy = 50;
// Delay getting the position to ensure that the UI has finished updating
await Task.Delay(2000);
var position = await locator.GetPositionAsync(); var position = await locator.GetPositionAsync();
_settingsService.Latitude = position.Latitude.ToString(); _settingsService.Latitude = position.Latitude.ToString();

View File

@ -13,16 +13,16 @@ namespace eShopOnContainers.Core.Services.Basket
BuyerId = "9245fe4a-d402-451c-b9ed-9c1a04247482", BuyerId = "9245fe4a-d402-451c-b9ed-9c1a04247482",
Items = new List<BasketItem> Items = new List<BasketItem>
{ {
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 = "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 = "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<CustomerBasket> GetBasketAsync(string guidUser, string token) public async Task<CustomerBasket> 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(); return new CustomerBasket();
} }
@ -32,7 +32,7 @@ namespace eShopOnContainers.Core.Services.Basket
public async Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token) public async Task<CustomerBasket> UpdateBasketAsync(CustomerBasket customerBasket, string token)
{ {
await Task.Delay(500); await Task.Delay(10);
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {
@ -46,7 +46,7 @@ namespace eShopOnContainers.Core.Services.Basket
public async Task ClearBasketAsync(string guidUser, string token) public async Task ClearBasketAsync(string guidUser, string token)
{ {
await Task.Delay(500); await Task.Delay(10);
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {

View File

@ -24,23 +24,23 @@ namespace eShopOnContainers.Core.Services.Catalog
private ObservableCollection<CatalogItem> MockCatalog = new ObservableCollection<CatalogItem> private ObservableCollection<CatalogItem> MockCatalog = new ObservableCollection<CatalogItem>
{ {
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.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.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.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.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.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<ObservableCollection<CatalogItem>> GetCatalogAsync() public async Task<ObservableCollection<CatalogItem>> GetCatalogAsync()
{ {
await Task.Delay(500); await Task.Delay(10);
return MockCatalog; return MockCatalog;
} }
public async Task<ObservableCollection<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId) public async Task<ObservableCollection<CatalogItem>> FilterAsync(int catalogBrandId, int catalogTypeId)
{ {
await Task.Delay(500); await Task.Delay(10);
return MockCatalog return MockCatalog
.Where(c => c.CatalogBrandId == catalogBrandId && .Where(c => c.CatalogBrandId == catalogBrandId &&
@ -50,14 +50,14 @@ namespace eShopOnContainers.Core.Services.Catalog
public async Task<ObservableCollection<CatalogBrand>> GetCatalogBrandAsync() public async Task<ObservableCollection<CatalogBrand>> GetCatalogBrandAsync()
{ {
await Task.Delay(500); await Task.Delay(10);
return MockCatalogBrand; return MockCatalogBrand;
} }
public async Task<ObservableCollection<CatalogType>> GetCatalogTypeAsync() public async Task<ObservableCollection<CatalogType>> GetCatalogTypeAsync()
{ {
await Task.Delay(500); await Task.Delay(10);
return MockCatalogType; return MockCatalogType;
} }

View File

@ -38,13 +38,13 @@ namespace eShopOnContainers.Core.Services.Marketing
public async Task<ObservableCollection<CampaignItem>> GetAllCampaignsAsync(string token) public async Task<ObservableCollection<CampaignItem>> GetAllCampaignsAsync(string token)
{ {
await Task.Delay(500); await Task.Delay(10);
return _mockCampaign; return _mockCampaign;
} }
public async Task<CampaignItem> GetCampaignByIdAsync(int campaignId, string token) public async Task<CampaignItem> GetCampaignByIdAsync(int campaignId, string token)
{ {
await Task.Delay(500); await Task.Delay(10);
return _mockCampaign.SingleOrDefault(c => c.Id == campaignId); return _mockCampaign.SingleOrDefault(c => c.Id == campaignId);
} }
} }

View File

@ -74,7 +74,7 @@ namespace eShopOnContainers.Core.Services.Order
public async Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token) public async Task<ObservableCollection<Models.Orders.Order>> GetOrdersAsync(string token)
{ {
await Task.Delay(500); await Task.Delay(10);
if (!string.IsNullOrEmpty(token)) if (!string.IsNullOrEmpty(token))
{ {
@ -88,7 +88,7 @@ namespace eShopOnContainers.Core.Services.Order
public async Task<Models.Orders.Order> GetOrderAsync(int orderId, string token) public async Task<Models.Orders.Order> GetOrderAsync(int orderId, string token)
{ {
await Task.Delay(500); await Task.Delay(10);
if (!string.IsNullOrEmpty(token)) if (!string.IsNullOrEmpty(token))
return MockOrders return MockOrders
@ -99,7 +99,7 @@ namespace eShopOnContainers.Core.Services.Order
public async Task CreateOrderAsync(Models.Orders.Order newOrder, string token) public async Task CreateOrderAsync(Models.Orders.Order newOrder, string token)
{ {
await Task.Delay(500); await Task.Delay(10);
if (!string.IsNullOrEmpty(token)) if (!string.IsNullOrEmpty(token))
{ {

View File

@ -28,7 +28,7 @@ namespace eShopOnContainers.Core.Services.User
public async Task<UserInfo> GetUserInfoAsync(string authToken) public async Task<UserInfo> GetUserInfoAsync(string authToken)
{ {
await Task.Delay(500); await Task.Delay(10);
return MockUserInfo; return MockUserInfo;
} }
} }

View File

@ -160,7 +160,7 @@ namespace eShopOnContainers.Core.ViewModels
{ {
try try
{ {
await Task.Delay(1000); await Task.Delay(10);
isAuthenticated = true; isAuthenticated = true;
} }
@ -189,7 +189,7 @@ namespace eShopOnContainers.Core.ViewModels
{ {
IsBusy = true; IsBusy = true;
await Task.Delay(500); await Task.Delay(10);
LoginUrl = _identityService.CreateAuthorizationRequest(); LoginUrl = _identityService.CreateAuthorizationRequest();

View File

@ -10,24 +10,19 @@ namespace eShopOnContainers.iOS.Services
{ {
internal class GeolocationSingleUpdateDelegate : CLLocationManagerDelegate internal class GeolocationSingleUpdateDelegate : CLLocationManagerDelegate
{ {
bool _haveHeading;
bool _haveLocation; bool _haveLocation;
readonly Position _position = new Position(); readonly Position _position = new Position();
CLHeading _bestHeading;
readonly double _desiredAccuracy; readonly double _desiredAccuracy;
readonly bool _includeHeading;
readonly TaskCompletionSource<Position> _tcs; readonly TaskCompletionSource<Position> _tcs;
readonly CLLocationManager _manager; readonly CLLocationManager _manager;
public Task<Position> Task => _tcs?.Task; public Task<Position> 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; _manager = manager;
_tcs = new TaskCompletionSource<Position>(manager); _tcs = new TaskCompletionSource<Position>(manager);
_desiredAccuracy = desiredAccuracy; _desiredAccuracy = desiredAccuracy;
_includeHeading = includeHeading;
if (timeout != Timeout.Infinite) 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) public override void UpdatedLocation(CLLocationManager manager, CLLocation newLocation, CLLocation oldLocation)
{ {
if (newLocation.HorizontalAccuracy < 0) if (newLocation.HorizontalAccuracy < 0)
@ -97,31 +90,13 @@ namespace eShopOnContainers.iOS.Services
{ {
_position.Timestamp = new DateTimeOffset((DateTime)newLocation.Timestamp); _position.Timestamp = new DateTimeOffset((DateTime)newLocation.Timestamp);
} }
catch (Exception ex) catch (Exception)
{ {
_position.Timestamp = DateTimeOffset.UtcNow; _position.Timestamp = DateTimeOffset.UtcNow;
} }
_haveLocation = true; _haveLocation = true;
if ((!_includeHeading || _haveHeading) && _position.Accuracy <= _desiredAccuracy) if (_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)
{ {
_tcs.TrySetResult(new Position(_position)); _tcs.TrySetResult(new Position(_position));
StopListening(); StopListening();
@ -130,9 +105,6 @@ namespace eShopOnContainers.iOS.Services
private void StopListening() private void StopListening()
{ {
if (CLLocationManager.HeadingAvailable)
_manager.StopUpdatingHeading();
_manager.StopUpdatingLocation(); _manager.StopUpdatingLocation();
} }
} }

View File

@ -15,12 +15,6 @@ namespace eShopOnContainers.iOS.Services
{ {
public class LocationServiceImplementation : ILocationServiceImplementation public class LocationServiceImplementation : ILocationServiceImplementation
{ {
bool _deferringUpdates;
readonly CLLocationManager _manager;
Position _lastPosition;
public event EventHandler<PositionErrorEventArgs> PositionError;
public event EventHandler<PositionEventArgs> PositionChanged;
public double DesiredAccuracy { get; set; } public double DesiredAccuracy { get; set; }
public bool IsGeolocationAvailable => true; public bool IsGeolocationAvailable => true;
public bool IsGeolocationEnabled public bool IsGeolocationEnabled
@ -32,21 +26,11 @@ namespace eShopOnContainers.iOS.Services
} }
} }
public bool SupportsHeading => CLLocationManager.HeadingAvailable;
public LocationServiceImplementation() public LocationServiceImplementation()
{ {
DesiredAccuracy = 100; 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 #region Internal Implementation
async Task<bool> CheckPermissions(Permission permission) async Task<bool> CheckPermissions(Permission permission)
@ -78,7 +62,7 @@ namespace eShopOnContainers.iOS.Services
#region ILocationServiceImplementation #region ILocationServiceImplementation
public async Task<Position> GetPositionAsync(TimeSpan? timeout, CancellationToken? cancelToken = null, bool includeHeading = false) public async Task<Position> GetPositionAsync(TimeSpan? timeout, CancellationToken? cancelToken = null)
{ {
var permission = Permission.LocationWhenInUse; var permission = Permission.LocationWhenInUse;
var hasPermission = await CheckPermissions(permission); var hasPermission = await CheckPermissions(permission);
@ -96,53 +80,16 @@ namespace eShopOnContainers.iOS.Services
var manager = GetManager(); var manager = GetManager();
manager.DesiredAccuracy = DesiredAccuracy; 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 // Always prevent location update pausing since we're only listening for a single update
if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0)) if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0))
manager.PausesLocationUpdatesAutomatically = false; manager.PausesLocationUpdatesAutomatically = false;
tcs = new TaskCompletionSource<Position>(manager); tcs = new TaskCompletionSource<Position>(manager);
var singleListener = new GeolocationSingleUpdateDelegate(manager, DesiredAccuracy, includeHeading, timeoutMilliseconds, cancelToken.Value); var singleListener = new GeolocationSingleUpdateDelegate(manager, DesiredAccuracy, timeoutMilliseconds, cancelToken.Value);
manager.Delegate = singleListener; manager.Delegate = singleListener;
manager.StartUpdatingLocation(); manager.StartUpdatingLocation();
if (includeHeading && SupportsHeading)
manager.StartUpdatingHeading();
return await singleListener.Task; return await singleListener.Task;
//tcs = new TaskCompletionSource<Position>();
//if (_lastPosition == null)
//{
// if (cancelToken != CancellationToken.None)
// cancelToken.Value.Register(() => tcs.TrySetCanceled());
// EventHandler<PositionErrorEventArgs> gotError = null;
// gotError = (s, e) =>
// {
// tcs.TrySetException(new GeolocationException(e.Error));
// PositionError -= gotError;
// };
// PositionError += gotError;
// EventHandler<PositionEventArgs> gotPosition = null;
// gotPosition = (s, e) =>
// {
// tcs.TrySetResult(e.Position);
// PositionChanged += gotPosition;
// };
// PositionChanged += gotPosition;
//}
//else
// tcs.SetResult(_lastPosition);
//return await tcs.Task;
} }
#endregion #endregion

View File

@ -19,7 +19,6 @@ namespace eShopOnContainers.iOS.Services
return PermissionStatus.Disabled; return PermissionStatus.Disabled;
var status = CLLocationManager.Status; var status = CLLocationManager.Status;
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{ {
switch (status) switch (status)
@ -77,22 +76,6 @@ namespace eShopOnContainers.iOS.Services
locationManager.AuthorizationChanged += authCallback; locationManager.AuthorizationChanged += authCallback;
var info = NSBundle.MainBundle.InfoDictionary; 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 (permission == Permission.LocationWhenInUse)
{ {
if (info.ContainsKey(new NSString("NSLocationWhenInUseUsageDescription"))) if (info.ContainsKey(new NSString("NSLocationWhenInUseUsageDescription")))