commit
e4107260db
@ -3,7 +3,7 @@
|
|||||||
The ARM template `sbusdeploy.json` and its parameter file (`sbusdeploy.parameters.json`) are used to deploy following resources:
|
The ARM template `sbusdeploy.json` and its parameter file (`sbusdeploy.parameters.json`) are used to deploy following resources:
|
||||||
|
|
||||||
1. One Service Bus namespace
|
1. One Service Bus namespace
|
||||||
2. One Service Bus
|
2. One Service Bus topic
|
||||||
3. Subscriptions used by application
|
3. Subscriptions used by application
|
||||||
|
|
||||||
## Editing sbusdeploy.parameters.json file
|
## Editing sbusdeploy.parameters.json file
|
||||||
@ -17,17 +17,8 @@ be set is:
|
|||||||
|
|
||||||
Once parameter file is edited you can deploy it using [create-resources script](../readme.md).
|
Once parameter file is edited you can deploy it using [create-resources script](../readme.md).
|
||||||
|
|
||||||
i. e. if you are in windows, to deploy sql databases in a new resourcegroup located in westus, go to `deploy\az` folder and type:
|
i. e. if you are in windows, to deploy servicebus in a new resourcegroup located in westus, go to `deploy\az` folder and type:
|
||||||
|
|
||||||
```
|
```
|
||||||
create-resources.cmd servicebus\sbusdeploy newResourceGroup -c westus
|
create-resources.cmd servicebus\sbusdeploy newResourceGroup -c westus
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -6,29 +6,29 @@ metadata:
|
|||||||
app: eshop
|
app: eshop
|
||||||
data:
|
data:
|
||||||
# Basket.API entries
|
# Basket.API entries
|
||||||
BasketBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX in case of using Azure)
|
BasketBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
|
||||||
BasketRedisConStr: REDIS CONNECTION STRING FOR BASKET
|
BasketRedisConStr: REDIS CONNECTION STRING FOR BASKET
|
||||||
# Catalog.API entries
|
# Catalog.API entries
|
||||||
CatalogBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX in case of using Azure)
|
CatalogBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
|
||||||
CatalogSqlDb: Catalog SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
CatalogSqlDb: Catalog SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
||||||
# Identity.API entries
|
# Identity.API entries
|
||||||
IdentitySqlDb: Identity SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
IdentitySqlDb: Identity SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
||||||
# Locations.API entries
|
# Locations.API entries
|
||||||
LocationsBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX in case of using Azure)
|
LocationsBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
|
||||||
LocationsNoSqlDb: Locations MongoDb ConnectionString
|
LocationsNoSqlDb: Locations MongoDb ConnectionString
|
||||||
LocationsNoSqlDbName: Locations MongoDb database (LocationsDb)
|
LocationsNoSqlDbName: Locations MongoDb database (LocationsDb)
|
||||||
# Marketing.API entries
|
# Marketing.API entries
|
||||||
MarketingBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX in case of using Azure)
|
MarketingBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
|
||||||
MarketingNoSqlDb: Marketing MongoDb ConnectionString
|
MarketingNoSqlDb: Marketing MongoDb ConnectionString
|
||||||
MarketingNoSqlDbName: Marketing MongoDb database (MarketingDb)
|
MarketingNoSqlDbName: Marketing MongoDb database (MarketingDb)
|
||||||
MarketingSqlDb: Marketing SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
MarketingSqlDb: Marketing SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
||||||
# Ordering.API entries
|
# Ordering.API entries
|
||||||
OrderingBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX in case of using Azure)
|
OrderingBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
|
||||||
OrderingSqlDb: Ordering SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
OrderingSqlDb: Ordering SQL SERVER CONNECTION STRING (Server=xxxx;Intial Catalog=yyy;....)
|
||||||
GracePeriodManager_GracePeriodTime: "1"
|
GracePeriodManager_GracePeriodTime: "1"
|
||||||
GracePeriodManager_CheckUpdateTime: "15000"
|
GracePeriodManager_CheckUpdateTime: "15000"
|
||||||
# Payment.API entries
|
# Payment.API entries
|
||||||
PaymentBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX in case of using Azure)
|
PaymentBus: CONNECTION_STRING (NAME OF RABBITMQ CONTAINER OR Endpoint=sb://XXXX for topic in case of using Azure)
|
||||||
# Global entries
|
# Global entries
|
||||||
UseAzureServiceBus: "TRUE" IF USE AZURE SB ("FALSE" FOR USING RABBITMQ)
|
UseAzureServiceBus: "TRUE" IF USE AZURE SB ("FALSE" FOR USING RABBITMQ)
|
||||||
keystore: REDIS CONNECTION STRING FOR KEYSTORE
|
keystore: REDIS CONNECTION STRING FOR KEYSTORE
|
@ -1,9 +1,11 @@
|
|||||||
using eShopOnContainers.Core.Models.Location;
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using eShopOnContainers.Core.Services.Dependency;
|
||||||
using eShopOnContainers.Core.Services.Location;
|
using eShopOnContainers.Core.Services.Location;
|
||||||
using eShopOnContainers.Core.Services.Settings;
|
using eShopOnContainers.Core.Services.Settings;
|
||||||
using eShopOnContainers.Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
using eShopOnContainers.Services;
|
using eShopOnContainers.Services;
|
||||||
using Plugin.Geolocator;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@ -48,12 +50,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();
|
||||||
@ -69,18 +69,24 @@ namespace eShopOnContainers
|
|||||||
|
|
||||||
private async Task GetGpsLocation()
|
private async Task GetGpsLocation()
|
||||||
{
|
{
|
||||||
var locator = CrossGeolocator.Current;
|
var dependencyService = ViewModelLocator.Resolve<IDependencyService>();
|
||||||
|
var locator = dependencyService.Get<ILocationServiceImplementation>();
|
||||||
|
|
||||||
if (locator.IsGeolocationEnabled && locator.IsGeolocationAvailable)
|
if (locator.IsGeolocationEnabled && locator.IsGeolocationAvailable)
|
||||||
{
|
{
|
||||||
locator.AllowsBackgroundUpdates = true;
|
|
||||||
locator.DesiredAccuracy = 50;
|
locator.DesiredAccuracy = 50;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
var position = await locator.GetPositionAsync();
|
var position = await locator.GetPositionAsync();
|
||||||
|
|
||||||
_settingsService.Latitude = position.Latitude.ToString();
|
_settingsService.Latitude = position.Latitude.ToString();
|
||||||
_settingsService.Longitude = position.Longitude.ToString();
|
_settingsService.Longitude = position.Longitude.ToString();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_settingsService.AllowGpsLocation = false;
|
_settingsService.AllowGpsLocation = false;
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
namespace eShopOnContainers.Core.Models.Location
|
||||||
|
{
|
||||||
|
public enum GeolocationError
|
||||||
|
{
|
||||||
|
PositionUnavailable,
|
||||||
|
Unauthorized
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Core.Models.Location
|
||||||
|
{
|
||||||
|
public class GeolocationException : Exception
|
||||||
|
{
|
||||||
|
public GeolocationError Error { get; private set; }
|
||||||
|
|
||||||
|
public GeolocationException(GeolocationError error)
|
||||||
|
: base("A geolocation error occured: " + error)
|
||||||
|
{
|
||||||
|
if (!Enum.IsDefined(typeof(GeolocationError), error))
|
||||||
|
throw new ArgumentException("error is not a valid GelocationError member", "error");
|
||||||
|
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeolocationException(GeolocationError error, Exception innerException)
|
||||||
|
: base("A geolocation error occured: " + error, innerException)
|
||||||
|
{
|
||||||
|
if (!Enum.IsDefined(typeof(GeolocationError), error))
|
||||||
|
throw new ArgumentException("error is not a valid GelocationError member", "error");
|
||||||
|
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Core.Models.Location
|
||||||
|
{
|
||||||
|
public class Position
|
||||||
|
{
|
||||||
|
public DateTimeOffset Timestamp { get; set; }
|
||||||
|
public double Latitude { get; set; }
|
||||||
|
public double Longitude { get; set; }
|
||||||
|
public double Altitude { get; set; }
|
||||||
|
public double Accuracy { get; set; }
|
||||||
|
public double AltitudeAccuracy { get; set; }
|
||||||
|
public double Heading { get; set; }
|
||||||
|
public double Speed { get; set; }
|
||||||
|
|
||||||
|
public Position()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(double latitude, double longitude)
|
||||||
|
{
|
||||||
|
|
||||||
|
Timestamp = DateTimeOffset.UtcNow;
|
||||||
|
Latitude = latitude;
|
||||||
|
Longitude = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(Position position)
|
||||||
|
{
|
||||||
|
if (position == null)
|
||||||
|
throw new ArgumentNullException("position");
|
||||||
|
|
||||||
|
Timestamp = position.Timestamp;
|
||||||
|
Latitude = position.Latitude;
|
||||||
|
Longitude = position.Longitude;
|
||||||
|
Altitude = position.Altitude;
|
||||||
|
AltitudeAccuracy = position.AltitudeAccuracy;
|
||||||
|
Accuracy = position.Accuracy;
|
||||||
|
Heading = position.Heading;
|
||||||
|
Speed = position.Speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
namespace eShopOnContainers.Core.Models.Permissions
|
||||||
|
{
|
||||||
|
public enum Permission
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Location,
|
||||||
|
LocationAlways,
|
||||||
|
LocationWhenInUse
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
namespace eShopOnContainers.Core.Models.Permissions
|
||||||
|
{
|
||||||
|
public enum PermissionStatus
|
||||||
|
{
|
||||||
|
Denied,
|
||||||
|
Disabled,
|
||||||
|
Granted,
|
||||||
|
Restricted,
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
}
|
@ -20,9 +20,9 @@ namespace eShopOnContainers.Core.Services.Basket
|
|||||||
|
|
||||||
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))
|
||||||
{
|
{
|
||||||
|
@ -33,14 +33,14 @@ namespace eShopOnContainers.Core.Services.Catalog
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Core.Services.Location
|
||||||
|
{
|
||||||
|
public interface ILocationServiceImplementation
|
||||||
|
{
|
||||||
|
double DesiredAccuracy { get; set; }
|
||||||
|
bool IsGeolocationAvailable { get; }
|
||||||
|
bool IsGeolocationEnabled { get; }
|
||||||
|
|
||||||
|
Task<Position> GetPositionAsync(TimeSpan? timeout = null, CancellationToken? token = null);
|
||||||
|
}
|
||||||
|
}
|
@ -16,11 +16,8 @@ namespace eShopOnContainers.Core.Services.Location
|
|||||||
public async Task UpdateUserLocation(eShopOnContainers.Core.Models.Location.Location newLocReq, string token)
|
public async Task UpdateUserLocation(eShopOnContainers.Core.Models.Location.Location newLocReq, string token)
|
||||||
{
|
{
|
||||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.LocationEndpoint);
|
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.LocationEndpoint);
|
||||||
|
|
||||||
builder.Path = "api/v1/locations";
|
builder.Path = "api/v1/locations";
|
||||||
|
|
||||||
string uri = builder.ToString();
|
string uri = builder.ToString();
|
||||||
|
|
||||||
await _requestProvider.PostAsync(uri, newLocReq, token);
|
await _requestProvider.PostAsync(uri, newLocReq, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using eShopOnContainers.Core.Models.Permissions;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Core.Services.Permissions
|
||||||
|
{
|
||||||
|
public interface IPermissionsService
|
||||||
|
{
|
||||||
|
Task<PermissionStatus> CheckPermissionStatusAsync(Permission permission);
|
||||||
|
Task<Dictionary<Permission, PermissionStatus>> RequestPermissionsAsync(params Permission[] permissions);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -3,11 +3,11 @@ using eShopOnContainers.Core.Models.User;
|
|||||||
using eShopOnContainers.Core.Services.Location;
|
using eShopOnContainers.Core.Services.Location;
|
||||||
using eShopOnContainers.Core.Services.Settings;
|
using eShopOnContainers.Core.Services.Settings;
|
||||||
using eShopOnContainers.Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
using Plugin.Geolocator;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
using eShopOnContainers.Core.Services.Dependency;
|
||||||
|
|
||||||
namespace eShopOnContainers.Core.ViewModels
|
namespace eShopOnContainers.Core.ViewModels
|
||||||
{
|
{
|
||||||
@ -29,11 +29,13 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
|
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly ILocationService _locationService;
|
private readonly ILocationService _locationService;
|
||||||
|
private readonly IDependencyService _dependencyService;
|
||||||
|
|
||||||
public SettingsViewModel(ISettingsService settingsService, ILocationService locationService)
|
public SettingsViewModel(ISettingsService settingsService, ILocationService locationService, IDependencyService dependencyService)
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_locationService = locationService;
|
_locationService = locationService;
|
||||||
|
_dependencyService = dependencyService;
|
||||||
|
|
||||||
_useAzureServices = !_settingsService.UseMocks;
|
_useAzureServices = !_settingsService.UseMocks;
|
||||||
_endpoint = _settingsService.UrlBase;
|
_endpoint = _settingsService.UrlBase;
|
||||||
@ -308,7 +310,6 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void UpdateUseAzureServices()
|
private void UpdateUseAzureServices()
|
||||||
{
|
{
|
||||||
// Save use mocks services to local storage
|
// Save use mocks services to local storage
|
||||||
@ -342,7 +343,7 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
{
|
{
|
||||||
if (_allowGpsLocation)
|
if (_allowGpsLocation)
|
||||||
{
|
{
|
||||||
var locator = CrossGeolocator.Current;
|
var locator = _dependencyService.Get<ILocationServiceImplementation>();
|
||||||
if (!locator.IsGeolocationEnabled)
|
if (!locator.IsGeolocationEnabled)
|
||||||
{
|
{
|
||||||
_allowGpsLocation = false;
|
_allowGpsLocation = false;
|
||||||
|
@ -10,9 +10,12 @@
|
|||||||
<PackageReference Include="SlideOverKit" Version="2.1.5" />
|
<PackageReference Include="SlideOverKit" Version="2.1.5" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||||
<PackageReference Include="PCLCrypto" Version="2.0.147" />
|
<PackageReference Include="PCLCrypto" Version="2.0.147" />
|
||||||
<PackageReference Include="Xam.Plugin.Geolocator" Version="3.0.4" />
|
|
||||||
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.3.4" />
|
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.3.4" />
|
||||||
<PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
|
<PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
|
||||||
<PackageReference Include="IdentityModel" Version="3.0.0" />
|
<PackageReference Include="IdentityModel" Version="3.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Services\Permissions\" />
|
||||||
|
<Folder Include="Models\Permissions\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -7,9 +7,9 @@ using Android.Runtime;
|
|||||||
using Android.Views;
|
using Android.Views;
|
||||||
using FFImageLoading;
|
using FFImageLoading;
|
||||||
using FFImageLoading.Forms.Droid;
|
using FFImageLoading.Forms.Droid;
|
||||||
using Plugin.Permissions;
|
|
||||||
using System;
|
using System;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
using eShopOnContainers.Droid.Services;
|
||||||
|
|
||||||
namespace eShopOnContainers.Droid.Activities
|
namespace eShopOnContainers.Droid.Activities
|
||||||
{
|
{
|
||||||
@ -57,7 +57,7 @@ namespace eShopOnContainers.Droid.Activities
|
|||||||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
|
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
|
||||||
{
|
{
|
||||||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
((PermissionsService)PermissionsService.Instance).OnRequestPermissionResult(requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Droid.Extensions
|
||||||
|
{
|
||||||
|
public static class LocationExtensions
|
||||||
|
{
|
||||||
|
static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
|
static int TwoMinutes = 120000;
|
||||||
|
|
||||||
|
internal static Position ToPosition(this Android.Locations.Location location)
|
||||||
|
{
|
||||||
|
var p = new Position();
|
||||||
|
if (location.HasAccuracy)
|
||||||
|
p.Accuracy = location.Accuracy;
|
||||||
|
if (location.HasAltitude)
|
||||||
|
p.Altitude = location.Altitude;
|
||||||
|
if (location.HasBearing)
|
||||||
|
p.Heading = location.Bearing;
|
||||||
|
if (location.HasSpeed)
|
||||||
|
p.Speed = location.Speed;
|
||||||
|
|
||||||
|
p.Longitude = location.Longitude;
|
||||||
|
p.Latitude = location.Latitude;
|
||||||
|
p.Timestamp = location.GetTimestamp();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static DateTimeOffset GetTimestamp(this Android.Locations.Location location)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new DateTimeOffset(Epoch.AddMilliseconds(location.Time));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return new DateTimeOffset(Epoch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsBetterLocation(this Android.Locations.Location location, Android.Locations.Location bestLocation)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (bestLocation == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var timeDelta = location.Time - bestLocation.Time;
|
||||||
|
var isSignificantlyNewer = timeDelta > TwoMinutes;
|
||||||
|
var isSignificantlyOlder = timeDelta < -TwoMinutes;
|
||||||
|
var isNewer = timeDelta > 0;
|
||||||
|
|
||||||
|
if (isSignificantlyNewer)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (isSignificantlyOlder)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var accuracyDelta = (int)(location.Accuracy - bestLocation.Accuracy);
|
||||||
|
var isLessAccurate = accuracyDelta > 0;
|
||||||
|
var isMoreAccurate = accuracyDelta < 0;
|
||||||
|
var isSignificantlyLessAccurage = accuracyDelta > 200;
|
||||||
|
var isFromSameProvider = IsSameProvider(location.Provider, bestLocation.Provider);
|
||||||
|
|
||||||
|
if (isMoreAccurate)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (isNewer && !isLessAccurate)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (isNewer && !isSignificantlyLessAccurage && isFromSameProvider)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsSameProvider(string provider1, string provider2)
|
||||||
|
{
|
||||||
|
if (provider1 == null)
|
||||||
|
return provider2 == null;
|
||||||
|
|
||||||
|
return provider1.Equals(provider2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
using Android.App;
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Plugin.CurrentActivity;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace eShopOnContainers.Droid
|
namespace eShopOnContainers.Droid
|
||||||
@ -9,6 +9,8 @@ namespace eShopOnContainers.Droid
|
|||||||
[Application]
|
[Application]
|
||||||
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
|
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
|
||||||
{
|
{
|
||||||
|
internal static Context CurrentContext { get; private set; }
|
||||||
|
|
||||||
public MainApplication(IntPtr handle, JniHandleOwnership transer)
|
public MainApplication(IntPtr handle, JniHandleOwnership transer)
|
||||||
: base(handle, transer)
|
: base(handle, transer)
|
||||||
{
|
{
|
||||||
@ -28,7 +30,7 @@ namespace eShopOnContainers.Droid
|
|||||||
|
|
||||||
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
|
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
CrossCurrentActivity.Current.Activity = activity;
|
CurrentContext = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnActivityDestroyed(Activity activity)
|
public void OnActivityDestroyed(Activity activity)
|
||||||
@ -41,7 +43,7 @@ namespace eShopOnContainers.Droid
|
|||||||
|
|
||||||
public void OnActivityResumed(Activity activity)
|
public void OnActivityResumed(Activity activity)
|
||||||
{
|
{
|
||||||
CrossCurrentActivity.Current.Activity = activity;
|
CurrentContext = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
|
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
|
||||||
@ -50,7 +52,7 @@ namespace eShopOnContainers.Droid
|
|||||||
|
|
||||||
public void OnActivityStarted(Activity activity)
|
public void OnActivityStarted(Activity activity)
|
||||||
{
|
{
|
||||||
CrossCurrentActivity.Current.Activity = activity;
|
CurrentContext = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnActivityStopped(Activity activity)
|
public void OnActivityStopped(Activity activity)
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
using Android.Locations;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using eShopOnContainers.Droid.Extensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Droid.Services
|
||||||
|
{
|
||||||
|
public class GeolocationSingleListener : Java.Lang.Object, ILocationListener
|
||||||
|
{
|
||||||
|
readonly object _locationSync = new object();
|
||||||
|
readonly Action _finishedCallback;
|
||||||
|
readonly float _desiredAccuracy;
|
||||||
|
readonly Timer _timer;
|
||||||
|
readonly TaskCompletionSource<Position> _tcs = new TaskCompletionSource<Position>();
|
||||||
|
HashSet<string> _activeProviders = new HashSet<string>();
|
||||||
|
Android.Locations.Location _bestLocation;
|
||||||
|
|
||||||
|
public Task<Position> Task => _tcs.Task;
|
||||||
|
|
||||||
|
public GeolocationSingleListener(LocationManager manager, float desiredAccuracy, int timeout, IEnumerable<string> activeProviders, Action finishedCallback)
|
||||||
|
{
|
||||||
|
_desiredAccuracy = desiredAccuracy;
|
||||||
|
_finishedCallback = finishedCallback;
|
||||||
|
_activeProviders = new HashSet<string>(activeProviders);
|
||||||
|
|
||||||
|
foreach (var provider in activeProviders)
|
||||||
|
{
|
||||||
|
var location = manager.GetLastKnownLocation(provider);
|
||||||
|
if (location != null && location.IsBetterLocation(_bestLocation))
|
||||||
|
_bestLocation = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout != Timeout.Infinite)
|
||||||
|
_timer = new Timer(TimesUp, null, timeout, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel() => _tcs.TrySetCanceled();
|
||||||
|
|
||||||
|
public void OnLocationChanged(Android.Locations.Location location)
|
||||||
|
{
|
||||||
|
if (location.Accuracy <= _desiredAccuracy)
|
||||||
|
{
|
||||||
|
Finish(location);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_locationSync)
|
||||||
|
{
|
||||||
|
if (location.IsBetterLocation(_bestLocation))
|
||||||
|
_bestLocation = location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnProviderDisabled(string provider)
|
||||||
|
{
|
||||||
|
lock (_activeProviders)
|
||||||
|
{
|
||||||
|
if (_activeProviders.Remove(provider) && _activeProviders.Count == 0)
|
||||||
|
_tcs.TrySetException(new GeolocationException(GeolocationError.PositionUnavailable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnProviderEnabled(string provider)
|
||||||
|
{
|
||||||
|
lock (_activeProviders)
|
||||||
|
_activeProviders.Add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStatusChanged(string provider, [GeneratedEnum] Availability status, Bundle extras)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case Availability.Available:
|
||||||
|
OnProviderEnabled(provider);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Availability.OutOfService:
|
||||||
|
OnProviderDisabled(provider);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimesUp(object state)
|
||||||
|
{
|
||||||
|
lock (_locationSync)
|
||||||
|
{
|
||||||
|
if (_bestLocation == null)
|
||||||
|
{
|
||||||
|
if (_tcs.TrySetCanceled())
|
||||||
|
_finishedCallback?.Invoke();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Finish(_bestLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finish(Android.Locations.Location location)
|
||||||
|
{
|
||||||
|
_finishedCallback?.Invoke();
|
||||||
|
_tcs.TrySetResult(location.ToPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Locations;
|
||||||
|
using Android.OS;
|
||||||
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using eShopOnContainers.Core.Models.Permissions;
|
||||||
|
using eShopOnContainers.Core.Services.Location;
|
||||||
|
using eShopOnContainers.Droid.Services;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
[assembly: Xamarin.Forms.Dependency(typeof(LocationServiceImplementation))]
|
||||||
|
namespace eShopOnContainers.Droid.Services
|
||||||
|
{
|
||||||
|
public class LocationServiceImplementation : ILocationServiceImplementation
|
||||||
|
{
|
||||||
|
#region Internal Implementation
|
||||||
|
|
||||||
|
LocationManager _locationManager;
|
||||||
|
GeolocationSingleListener _singleListener = null;
|
||||||
|
|
||||||
|
string[] Providers => Manager.GetProviders(enabledOnly: false).ToArray();
|
||||||
|
string[] IgnoredProviders => new string[] { LocationManager.PassiveProvider, "local_database" };
|
||||||
|
|
||||||
|
public static string[] ProvidersToUse { get; set; } = new string[] { };
|
||||||
|
|
||||||
|
LocationManager Manager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_locationManager == null)
|
||||||
|
_locationManager = (LocationManager)Application.Context.GetSystemService(Context.LocationService);
|
||||||
|
return _locationManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocationServiceImplementation()
|
||||||
|
{
|
||||||
|
DesiredAccuracy = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<bool> CheckPermissionsAsync()
|
||||||
|
{
|
||||||
|
var status = await PermissionsService.Instance.CheckPermissionStatusAsync(Permission.Location);
|
||||||
|
if (status != PermissionStatus.Granted)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Currently do not have Location permissions, requesting permissions.");
|
||||||
|
|
||||||
|
var request = await PermissionsService.Instance.RequestPermissionsAsync(Permission.Location);
|
||||||
|
if (request[Permission.Location] != PermissionStatus.Granted)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Location permission denied.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ILocationServiceImplementation
|
||||||
|
|
||||||
|
public double DesiredAccuracy { get; set; }
|
||||||
|
|
||||||
|
public bool IsGeolocationAvailable => Providers.Length > 0;
|
||||||
|
|
||||||
|
public bool IsGeolocationEnabled => Providers.Any(p => !IgnoredProviders.Contains(p) && Manager.IsProviderEnabled(p));
|
||||||
|
|
||||||
|
public async Task<Position> GetPositionAsync(TimeSpan? timeout = null, CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
var timeoutMilliseconds = timeout.HasValue ? (int)timeout.Value.TotalMilliseconds : Timeout.Infinite;
|
||||||
|
if (timeoutMilliseconds <= 0 && timeoutMilliseconds != Timeout.Infinite)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(timeout), "Timeout must be greater than or equal to 0");
|
||||||
|
|
||||||
|
if (!cancelToken.HasValue)
|
||||||
|
cancelToken = CancellationToken.None;
|
||||||
|
|
||||||
|
var hasPermission = await CheckPermissionsAsync();
|
||||||
|
if (!hasPermission)
|
||||||
|
throw new GeolocationException(GeolocationError.Unauthorized);
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<Position>();
|
||||||
|
|
||||||
|
var providers = new List<string>();
|
||||||
|
if (ProvidersToUse == null || ProvidersToUse.Length == 0)
|
||||||
|
providers.AddRange(Providers);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var provider in Providers)
|
||||||
|
{
|
||||||
|
if (ProvidersToUse?.Contains(provider) ?? false)
|
||||||
|
continue;
|
||||||
|
providers.Add(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleListenerFinishCallback()
|
||||||
|
{
|
||||||
|
if (_singleListener == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < providers.Count; ++i)
|
||||||
|
Manager.RemoveUpdates(_singleListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
_singleListener = new GeolocationSingleListener(Manager, (float)DesiredAccuracy, timeoutMilliseconds, providers.Where(Manager.IsProviderEnabled), finishedCallback: SingleListenerFinishCallback);
|
||||||
|
if (cancelToken != CancellationToken.None)
|
||||||
|
{
|
||||||
|
cancelToken.Value.Register(() =>
|
||||||
|
{
|
||||||
|
_singleListener.Cancel();
|
||||||
|
|
||||||
|
for (int i = 0; i < providers.Count; ++i)
|
||||||
|
Manager.RemoveUpdates(_singleListener);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var looper = Looper.MyLooper() ?? Looper.MainLooper;
|
||||||
|
int enabled = 0;
|
||||||
|
for (var i = 0; i < providers.Count; ++i)
|
||||||
|
{
|
||||||
|
if (Manager.IsProviderEnabled(providers[i]))
|
||||||
|
enabled++;
|
||||||
|
|
||||||
|
Manager.RequestLocationUpdates(providers[i], 0, 0, _singleListener, looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabled == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < providers.Count; ++i)
|
||||||
|
Manager.RemoveUpdates(_singleListener);
|
||||||
|
|
||||||
|
tcs.SetException(new GeolocationException(GeolocationError.PositionUnavailable));
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Java.Lang.SecurityException ex)
|
||||||
|
{
|
||||||
|
tcs.SetException(new GeolocationException(GeolocationError.Unauthorized, ex));
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
return await _singleListener.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,241 @@
|
|||||||
|
using Android;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Support.V4.App;
|
||||||
|
using Android.Support.V4.Content;
|
||||||
|
using eShopOnContainers.Core.Models.Permissions;
|
||||||
|
using eShopOnContainers.Core.Services.Permissions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Droid.Services
|
||||||
|
{
|
||||||
|
public class PermissionsService : IPermissionsService
|
||||||
|
{
|
||||||
|
const int _permissionCode = 25;
|
||||||
|
object _locker = new object();
|
||||||
|
TaskCompletionSource<Dictionary<Permission, PermissionStatus>> _tcs;
|
||||||
|
Dictionary<Permission, PermissionStatus> _results;
|
||||||
|
IList<string> _requestedPermissions;
|
||||||
|
|
||||||
|
public static IPermissionsService Instance = new PermissionsService();
|
||||||
|
|
||||||
|
#region Internal Implementation
|
||||||
|
|
||||||
|
List<string> GetManifestNames(Permission permission)
|
||||||
|
{
|
||||||
|
var permissionNames = new List<string>();
|
||||||
|
switch (permission)
|
||||||
|
{
|
||||||
|
case Permission.LocationAlways:
|
||||||
|
case Permission.LocationWhenInUse:
|
||||||
|
case Permission.Location:
|
||||||
|
{
|
||||||
|
if (HasPermissionInManifest(Manifest.Permission.AccessCoarseLocation))
|
||||||
|
permissionNames.Add(Manifest.Permission.AccessCoarseLocation);
|
||||||
|
|
||||||
|
if (HasPermissionInManifest(Manifest.Permission.AccessFineLocation))
|
||||||
|
permissionNames.Add(Manifest.Permission.AccessFineLocation);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return permissionNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasPermissionInManifest(string permission)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_requestedPermissions != null)
|
||||||
|
return _requestedPermissions.Any(r => r.Equals(permission, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
|
||||||
|
// Try to use current activity else application context
|
||||||
|
var context = MainApplication.CurrentContext ?? Application.Context;
|
||||||
|
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Unable to detect current Activity or Application Context.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = context.PackageManager.GetPackageInfo(context.PackageName, Android.Content.PM.PackageInfoFlags.Permissions);
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Unable to get package info, will not be able to determine permissions to request.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_requestedPermissions = info.RequestedPermissions;
|
||||||
|
if (_requestedPermissions == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("There are no requested permissions, please check to ensure you have marked the permissions that you want to request.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _requestedPermissions.Any(r => r.Equals(permission, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Write("Unable to check manifest for permission: " + ex);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Permission GetPermissionForManifestName(string permission)
|
||||||
|
{
|
||||||
|
switch (permission)
|
||||||
|
{
|
||||||
|
case Manifest.Permission.AccessCoarseLocation:
|
||||||
|
case Manifest.Permission.AccessFineLocation:
|
||||||
|
return Permission.Location;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Permission.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnRequestPermissionResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
|
||||||
|
{
|
||||||
|
if (requestCode != _permissionCode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_tcs == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (var i = 0; i < permissions.Length; i++)
|
||||||
|
{
|
||||||
|
if (_tcs.Task.Status == TaskStatus.Canceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var permission = GetPermissionForManifestName(permissions[i]);
|
||||||
|
if (permission == Permission.Unknown)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (permission == Permission.Location)
|
||||||
|
{
|
||||||
|
if (!_results.ContainsKey(Permission.LocationWhenInUse))
|
||||||
|
_results.Add(Permission.LocationWhenInUse, grantResults[i] == Android.Content.PM.Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_results.ContainsKey(permission))
|
||||||
|
_results.Add(permission, grantResults[i] == Android.Content.PM.Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_tcs.TrySetResult(_results);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IPermissionsService Implementation
|
||||||
|
|
||||||
|
public Task<PermissionStatus> CheckPermissionStatusAsync(Permission permission)
|
||||||
|
{
|
||||||
|
var names = GetManifestNames(permission);
|
||||||
|
if (names == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("No Android specific permissions needed for: " + permission);
|
||||||
|
return Task.FromResult(PermissionStatus.Granted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (names.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("No permissions found in manifest for: " + permission);
|
||||||
|
return Task.FromResult(PermissionStatus.Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = MainApplication.CurrentContext ?? Application.Context;
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Unable to detect current Activity or Application Context.");
|
||||||
|
return Task.FromResult(PermissionStatus.Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool targetsMOrHigher = context.ApplicationInfo.TargetSdkVersion >= Android.OS.BuildVersionCodes.M;
|
||||||
|
foreach (var name in names)
|
||||||
|
{
|
||||||
|
if (targetsMOrHigher)
|
||||||
|
{
|
||||||
|
if (ContextCompat.CheckSelfPermission(context, name) != Android.Content.PM.Permission.Granted)
|
||||||
|
return Task.FromResult(PermissionStatus.Denied);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (PermissionChecker.CheckSelfPermission(context, name) != PermissionChecker.PermissionGranted)
|
||||||
|
return Task.FromResult(PermissionStatus.Denied);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Task.FromResult(PermissionStatus.Granted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<Permission, PermissionStatus>> RequestPermissionsAsync(params Permission[] permissions)
|
||||||
|
{
|
||||||
|
if (_tcs != null && !_tcs.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
_tcs.SetCanceled();
|
||||||
|
_tcs = null;
|
||||||
|
}
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_results = new Dictionary<Permission, PermissionStatus>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = MainApplication.CurrentContext;
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Unable to detect current Activity.");
|
||||||
|
foreach (var permission in permissions)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (!_results.ContainsKey(permission))
|
||||||
|
_results.Add(permission, PermissionStatus.Unknown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _results;
|
||||||
|
}
|
||||||
|
|
||||||
|
var permissionsToRequest = new List<string>();
|
||||||
|
foreach (var permission in permissions)
|
||||||
|
{
|
||||||
|
var result = await CheckPermissionStatusAsync(permission).ConfigureAwait(false);
|
||||||
|
if (result != PermissionStatus.Granted)
|
||||||
|
{
|
||||||
|
var names = GetManifestNames(permission);
|
||||||
|
if ((names?.Count ?? 0) == 0)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (!_results.ContainsKey(permission))
|
||||||
|
_results.Add(permission, PermissionStatus.Unknown);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionsToRequest.AddRange(names);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (!_results.ContainsKey(permission))
|
||||||
|
_results.Add(permission, PermissionStatus.Granted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissionsToRequest.Count == 0)
|
||||||
|
return _results;
|
||||||
|
|
||||||
|
_tcs = new TaskCompletionSource<Dictionary<Permission, PermissionStatus>>();
|
||||||
|
ActivityCompat.RequestPermissions((Activity)context, permissionsToRequest.ToArray(), _permissionCode);
|
||||||
|
return await _tcs.Task.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -145,27 +145,12 @@
|
|||||||
<Reference Include="Acr.UserDialogs.Interface">
|
<Reference Include="Acr.UserDialogs.Interface">
|
||||||
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\MonoAndroid10\Acr.UserDialogs.Interface.dll</HintPath>
|
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\MonoAndroid10\Acr.UserDialogs.Interface.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Plugin.CurrentActivity">
|
|
||||||
<HintPath>..\..\..\..\packages\Plugin.CurrentActivity.1.0.1\lib\MonoAndroid10\Plugin.CurrentActivity.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Permissions.Abstractions">
|
|
||||||
<HintPath>..\..\..\..\packages\Plugin.Permissions.2.2.1\lib\MonoAndroid10\Plugin.Permissions.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Permissions">
|
|
||||||
<HintPath>..\..\..\..\packages\Plugin.Permissions.2.2.1\lib\MonoAndroid10\Plugin.Permissions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SlideOverKit">
|
<Reference Include="SlideOverKit">
|
||||||
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\MonoAndroid10\SlideOverKit.dll</HintPath>
|
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\MonoAndroid10\SlideOverKit.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SlideOverKit.Droid">
|
<Reference Include="SlideOverKit.Droid">
|
||||||
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\MonoAndroid10\SlideOverKit.Droid.dll</HintPath>
|
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\MonoAndroid10\SlideOverKit.Droid.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Plugin.Geolocator.Abstractions">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\MonoAndroid10\Plugin.Geolocator.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Geolocator">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\MonoAndroid10\Plugin.Geolocator.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System.Net.Http.Extensions">
|
<Reference Include="System.Net.Http.Extensions">
|
||||||
<HintPath>..\..\..\..\packages\Microsoft.Net.Http.2.2.28\lib\monoandroid\System.Net.Http.Extensions.dll</HintPath>
|
<HintPath>..\..\..\..\packages\Microsoft.Net.Http.2.2.28\lib\monoandroid\System.Net.Http.Extensions.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@ -227,6 +212,10 @@
|
|||||||
<Compile Include="Effects\BaseContainerEffect.cs" />
|
<Compile Include="Effects\BaseContainerEffect.cs" />
|
||||||
<Compile Include="Activities\SplashActivity.cs" />
|
<Compile Include="Activities\SplashActivity.cs" />
|
||||||
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
||||||
|
<Compile Include="Services\PermissionsService.cs" />
|
||||||
|
<Compile Include="Services\LocationServiceImplementation.cs" />
|
||||||
|
<Compile Include="Services\GeolocationSingleListener.cs" />
|
||||||
|
<Compile Include="Extensions\LocationExtensions.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="..\CommonResources\Fonts\Montserrat-Bold.ttf">
|
<AndroidAsset Include="..\CommonResources\Fonts\Montserrat-Bold.ttf">
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
<package id="PInvoke.Kernel32" version="0.3.2" targetFramework="monoandroid80" />
|
<package id="PInvoke.Kernel32" version="0.3.2" targetFramework="monoandroid80" />
|
||||||
<package id="PInvoke.NCrypt" version="0.3.2" targetFramework="monoandroid80" />
|
<package id="PInvoke.NCrypt" version="0.3.2" targetFramework="monoandroid80" />
|
||||||
<package id="PInvoke.Windows.Core" version="0.3.2" targetFramework="monoandroid80" />
|
<package id="PInvoke.Windows.Core" version="0.3.2" targetFramework="monoandroid80" />
|
||||||
<package id="Plugin.CurrentActivity" version="1.0.1" targetFramework="monoandroid80" />
|
|
||||||
<package id="Plugin.Permissions" version="2.2.1" targetFramework="monoandroid80" />
|
|
||||||
<package id="SlideOverKit" version="2.1.5" targetFramework="monoandroid80" />
|
<package id="SlideOverKit" version="2.1.5" targetFramework="monoandroid80" />
|
||||||
<package id="Splat" version="2.0.0" targetFramework="monoandroid80" />
|
<package id="Splat" version="2.0.0" targetFramework="monoandroid80" />
|
||||||
<package id="System.AppContext" version="4.3.0" targetFramework="monoandroid80" />
|
<package id="System.AppContext" version="4.3.0" targetFramework="monoandroid80" />
|
||||||
@ -69,7 +67,6 @@
|
|||||||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="monoandroid80" />
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="monoandroid80" />
|
||||||
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="monoandroid80" />
|
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="monoandroid80" />
|
||||||
<package id="Validation" version="2.2.8" targetFramework="monoandroid80" />
|
<package id="Validation" version="2.2.8" targetFramework="monoandroid80" />
|
||||||
<package id="Xam.Plugin.Geolocator" version="3.0.4" targetFramework="monoandroid80" />
|
|
||||||
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="25.4.0.2" targetFramework="monoandroid80" />
|
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||||
<package id="Xamarin.Android.Support.Annotations" version="25.4.0.2" targetFramework="monoandroid80" />
|
<package id="Xamarin.Android.Support.Annotations" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||||
<package id="Xamarin.Android.Support.Compat" version="25.4.0.2" targetFramework="monoandroid80" />
|
<package id="Xamarin.Android.Support.Compat" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Android.App;
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Plugin.CurrentActivity;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace eShopOnContainers.TestRunner.Droid
|
namespace eShopOnContainers.TestRunner.Droid
|
||||||
@ -10,8 +10,10 @@ namespace eShopOnContainers.TestRunner.Droid
|
|||||||
[Application]
|
[Application]
|
||||||
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
|
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
|
||||||
{
|
{
|
||||||
|
internal static Context CurrentContext { get; private set; }
|
||||||
|
|
||||||
public MainApplication(IntPtr handle, JniHandleOwnership transer)
|
public MainApplication(IntPtr handle, JniHandleOwnership transer)
|
||||||
:base(handle, transer)
|
: base(handle, transer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +32,7 @@ namespace eShopOnContainers.TestRunner.Droid
|
|||||||
|
|
||||||
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
|
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
CrossCurrentActivity.Current.Activity = activity;
|
CurrentContext = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnActivityDestroyed(Activity activity)
|
public void OnActivityDestroyed(Activity activity)
|
||||||
@ -43,7 +45,7 @@ namespace eShopOnContainers.TestRunner.Droid
|
|||||||
|
|
||||||
public void OnActivityResumed(Activity activity)
|
public void OnActivityResumed(Activity activity)
|
||||||
{
|
{
|
||||||
CrossCurrentActivity.Current.Activity = activity;
|
CurrentContext = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
|
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
|
||||||
@ -52,7 +54,7 @@ namespace eShopOnContainers.TestRunner.Droid
|
|||||||
|
|
||||||
public void OnActivityStarted(Activity activity)
|
public void OnActivityStarted(Activity activity)
|
||||||
{
|
{
|
||||||
CrossCurrentActivity.Current.Activity = activity;
|
CurrentContext = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnActivityStopped(Activity activity)
|
public void OnActivityStopped(Activity activity)
|
||||||
|
@ -52,12 +52,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Mono.Android" />
|
<Reference Include="Mono.Android" />
|
||||||
<Reference Include="mscorlib" />
|
<Reference Include="mscorlib" />
|
||||||
<Reference Include="Plugin.Permissions, Version=2.2.1.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Plugin.Permissions.2.2.1\lib\MonoAndroid10\Plugin.Permissions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Permissions.Abstractions, Version=2.2.1.0, Culture=neutral, processorArchitecture=MSIL">
|
|
||||||
<HintPath>..\..\..\..\packages\Plugin.Permissions.2.2.1\lib\MonoAndroid10\Plugin.Permissions.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
@ -157,15 +151,6 @@
|
|||||||
<Reference Include="Plugin.Settings">
|
<Reference Include="Plugin.Settings">
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\MonoAndroid10\Plugin.Settings.dll</HintPath>
|
<HintPath>..\..\..\..\packages\Xam.Plugins.Settings.3.1.1\lib\MonoAndroid10\Plugin.Settings.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Plugin.CurrentActivity">
|
|
||||||
<HintPath>..\..\..\..\packages\Plugin.CurrentActivity.1.0.1\lib\MonoAndroid10\Plugin.CurrentActivity.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Geolocator.Abstractions">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\MonoAndroid10\Plugin.Geolocator.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Geolocator">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\MonoAndroid10\Plugin.Geolocator.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="PInvoke.Windows.Core">
|
<Reference Include="PInvoke.Windows.Core">
|
||||||
<HintPath>..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
|
<HintPath>..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
<package id="PInvoke.Kernel32" version="0.3.2" targetFramework="monoandroid80" />
|
<package id="PInvoke.Kernel32" version="0.3.2" targetFramework="monoandroid80" />
|
||||||
<package id="PInvoke.NCrypt" version="0.3.2" targetFramework="monoandroid80" />
|
<package id="PInvoke.NCrypt" version="0.3.2" targetFramework="monoandroid80" />
|
||||||
<package id="PInvoke.Windows.Core" version="0.3.2" targetFramework="monoandroid80" />
|
<package id="PInvoke.Windows.Core" version="0.3.2" targetFramework="monoandroid80" />
|
||||||
<package id="Plugin.CurrentActivity" version="1.0.1" targetFramework="monoandroid80" />
|
|
||||||
<package id="Plugin.Permissions" version="2.2.1" targetFramework="monoandroid80" />
|
|
||||||
<package id="SlideOverKit" version="2.1.5" targetFramework="monoandroid80" />
|
<package id="SlideOverKit" version="2.1.5" targetFramework="monoandroid80" />
|
||||||
<package id="Splat" version="2.0.0" targetFramework="monoandroid80" />
|
<package id="Splat" version="2.0.0" targetFramework="monoandroid80" />
|
||||||
<package id="System.AppContext" version="4.3.0" targetFramework="monoandroid80" />
|
<package id="System.AppContext" version="4.3.0" targetFramework="monoandroid80" />
|
||||||
@ -66,7 +64,6 @@
|
|||||||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="monoandroid80" />
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="monoandroid80" />
|
||||||
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="monoandroid80" />
|
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="monoandroid80" />
|
||||||
<package id="Validation" version="2.2.8" targetFramework="monoandroid80" />
|
<package id="Validation" version="2.2.8" targetFramework="monoandroid80" />
|
||||||
<package id="Xam.Plugin.Geolocator" version="3.0.4" targetFramework="monoandroid80" />
|
|
||||||
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="25.4.0.2" targetFramework="monoandroid80" />
|
<package id="Xamarin.Android.Support.Animated.Vector.Drawable" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||||
<package id="Xamarin.Android.Support.Annotations" version="25.4.0.2" targetFramework="monoandroid80" />
|
<package id="Xamarin.Android.Support.Annotations" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||||
<package id="Xamarin.Android.Support.Compat" version="25.4.0.2" targetFramework="monoandroid80" />
|
<package id="Xamarin.Android.Support.Compat" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||||
|
@ -149,12 +149,6 @@
|
|||||||
<Reference Include="Newtonsoft.Json">
|
<Reference Include="Newtonsoft.Json">
|
||||||
<HintPath>..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\..\..\..\packages\Newtonsoft.Json.10.0.3\lib\netstandard1.3\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Plugin.Geolocator.Abstractions">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\Xamarin.iOS10\Plugin.Geolocator.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Geolocator">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\Xamarin.iOS10\Plugin.Geolocator.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="SlideOverKit">
|
<Reference Include="SlideOverKit">
|
||||||
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.dll</HintPath>
|
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\Xamarin.iOS10\SlideOverKit.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="xamarinios10" />
|
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="xamarinios10" />
|
||||||
<package id="Validation" version="2.2.8" targetFramework="xamarinios10" />
|
<package id="Validation" version="2.2.8" targetFramework="xamarinios10" />
|
||||||
<package id="WebP.Touch" version="1.0.7" targetFramework="xamarinios10" />
|
<package id="WebP.Touch" version="1.0.7" targetFramework="xamarinios10" />
|
||||||
<package id="Xam.Plugin.Geolocator" version="3.0.4" targetFramework="xamarinios10" />
|
|
||||||
<package id="Xamarin.FFImageLoading" version="2.3.4" targetFramework="xamarinios10" />
|
<package id="Xamarin.FFImageLoading" version="2.3.4" targetFramework="xamarinios10" />
|
||||||
<package id="Xamarin.FFImageLoading.Forms" version="2.3.4" targetFramework="xamarinios10" />
|
<package id="Xamarin.FFImageLoading.Forms" version="2.3.4" targetFramework="xamarinios10" />
|
||||||
<package id="Xamarin.Forms" version="2.5.0.122203" targetFramework="xamarinios10" />
|
<package id="Xamarin.Forms" version="2.5.0.122203" targetFramework="xamarinios10" />
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using eShopOnContainers.Core.Services.Location;
|
||||||
|
using eShopOnContainers.Windows.Services;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Windows.Devices.Geolocation;
|
||||||
|
using Windows.Foundation;
|
||||||
|
|
||||||
|
[assembly: Xamarin.Forms.Dependency(typeof(LocationServiceImplementation))]
|
||||||
|
namespace eShopOnContainers.Windows.Services
|
||||||
|
{
|
||||||
|
public class LocationServiceImplementation : ILocationServiceImplementation
|
||||||
|
{
|
||||||
|
double _desiredAccuracy;
|
||||||
|
Geolocator _locator = new Geolocator();
|
||||||
|
|
||||||
|
public LocationServiceImplementation()
|
||||||
|
{
|
||||||
|
DesiredAccuracy = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Internal Implementation
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ILocationServiceImplementation
|
||||||
|
|
||||||
|
public double DesiredAccuracy
|
||||||
|
{
|
||||||
|
get { return _desiredAccuracy; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_desiredAccuracy = value;
|
||||||
|
_locator.DesiredAccuracy = (value < 100) ? PositionAccuracy.High : PositionAccuracy.Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsGeolocationAvailable
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var status = _locator.LocationStatus;
|
||||||
|
while (status == PositionStatus.Initializing)
|
||||||
|
{
|
||||||
|
Task.Delay(10).Wait();
|
||||||
|
status = _locator.LocationStatus;
|
||||||
|
}
|
||||||
|
return status != PositionStatus.NotAvailable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsGeolocationEnabled
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var status = _locator.LocationStatus;
|
||||||
|
while (status == PositionStatus.Initializing)
|
||||||
|
{
|
||||||
|
Task.Delay(10).Wait();
|
||||||
|
status = _locator.LocationStatus;
|
||||||
|
}
|
||||||
|
return status != PositionStatus.Disabled && status != PositionStatus.NotAvailable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Position> GetPositionAsync(TimeSpan? timeout = null, CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
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 = _locator.GetGeopositionAsync(TimeSpan.FromTicks(0), TimeSpan.FromDays(365));
|
||||||
|
cancelToken.Value.Register(o => ((IAsyncOperation<Geoposition>)o).Cancel(), pos);
|
||||||
|
var timer = new eShopOnContainers.Windows.Helpers.Timeout(timeoutMilliseconds, pos.Cancel);
|
||||||
|
var tcs = new TaskCompletionSource<Position>();
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -115,12 +115,14 @@
|
|||||||
<Compile Include="Effects\EntryLineColorEffect.cs" />
|
<Compile Include="Effects\EntryLineColorEffect.cs" />
|
||||||
<Compile Include="Extensions\VisualTreeExtensions.cs" />
|
<Compile Include="Extensions\VisualTreeExtensions.cs" />
|
||||||
<Compile Include="Helpers\ColorHelper.cs" />
|
<Compile Include="Helpers\ColorHelper.cs" />
|
||||||
|
<Compile Include="Helpers\Timeout.cs" />
|
||||||
<Compile Include="MainPage.xaml.cs">
|
<Compile Include="MainPage.xaml.cs">
|
||||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Renderers\CustomTabbedPageRenderer.cs" />
|
<Compile Include="Renderers\CustomTabbedPageRenderer.cs" />
|
||||||
<Compile Include="Renderers\SlideDownMenuPageRenderer.cs" />
|
<Compile Include="Renderers\SlideDownMenuPageRenderer.cs" />
|
||||||
|
<Compile Include="Services\LocationServiceImplementation.cs" />
|
||||||
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
using CoreLocation;
|
||||||
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using Foundation;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.iOS.Services
|
||||||
|
{
|
||||||
|
internal class GeolocationSingleUpdateDelegate : CLLocationManagerDelegate
|
||||||
|
{
|
||||||
|
bool _haveLocation;
|
||||||
|
readonly Position _position = new Position();
|
||||||
|
readonly double _desiredAccuracy;
|
||||||
|
readonly TaskCompletionSource<Position> _tcs;
|
||||||
|
readonly CLLocationManager _manager;
|
||||||
|
|
||||||
|
public Task<Position> Task => _tcs?.Task;
|
||||||
|
|
||||||
|
public GeolocationSingleUpdateDelegate(CLLocationManager manager, double desiredAccuracy, int timeout, CancellationToken cancelToken)
|
||||||
|
{
|
||||||
|
_manager = manager;
|
||||||
|
_tcs = new TaskCompletionSource<Position>(manager);
|
||||||
|
_desiredAccuracy = desiredAccuracy;
|
||||||
|
|
||||||
|
if (timeout != Timeout.Infinite)
|
||||||
|
{
|
||||||
|
Timer t = null;
|
||||||
|
t = new Timer(s =>
|
||||||
|
{
|
||||||
|
if (_haveLocation)
|
||||||
|
_tcs.TrySetResult(new Position(_position));
|
||||||
|
else
|
||||||
|
_tcs.TrySetCanceled();
|
||||||
|
|
||||||
|
StopListening();
|
||||||
|
t.Dispose();
|
||||||
|
}, null, timeout, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelToken.Register(() =>
|
||||||
|
{
|
||||||
|
StopListening();
|
||||||
|
_tcs.TrySetCanceled();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AuthorizationChanged(CLLocationManager manager, CLAuthorizationStatus status)
|
||||||
|
{
|
||||||
|
// If user has services disabled, throw an exception for consistency.
|
||||||
|
if (status == CLAuthorizationStatus.Denied || status == CLAuthorizationStatus.Restricted)
|
||||||
|
{
|
||||||
|
StopListening();
|
||||||
|
_tcs.TrySetException(new GeolocationException(GeolocationError.Unauthorized));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Failed(CLLocationManager manager, NSError error)
|
||||||
|
{
|
||||||
|
switch ((CLError)(int)error.Code)
|
||||||
|
{
|
||||||
|
case CLError.Network:
|
||||||
|
StopListening();
|
||||||
|
_tcs.SetException(new GeolocationException(GeolocationError.PositionUnavailable));
|
||||||
|
break;
|
||||||
|
case CLError.LocationUnknown:
|
||||||
|
StopListening();
|
||||||
|
_tcs.TrySetException(new GeolocationException(GeolocationError.PositionUnavailable));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdatedLocation(CLLocationManager manager, CLLocation newLocation, CLLocation oldLocation)
|
||||||
|
{
|
||||||
|
if (newLocation.HorizontalAccuracy < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_haveLocation && newLocation.HorizontalAccuracy > _position.Accuracy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_position.Accuracy = newLocation.HorizontalAccuracy;
|
||||||
|
_position.Altitude = newLocation.Altitude;
|
||||||
|
_position.AltitudeAccuracy = newLocation.VerticalAccuracy;
|
||||||
|
_position.Latitude = newLocation.Coordinate.Latitude;
|
||||||
|
_position.Longitude = newLocation.Coordinate.Longitude;
|
||||||
|
_position.Speed = newLocation.Speed;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_position.Timestamp = new DateTimeOffset((DateTime)newLocation.Timestamp);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_position.Timestamp = DateTimeOffset.UtcNow;
|
||||||
|
}
|
||||||
|
_haveLocation = true;
|
||||||
|
|
||||||
|
if (_position.Accuracy <= _desiredAccuracy)
|
||||||
|
{
|
||||||
|
_tcs.TrySetResult(new Position(_position));
|
||||||
|
StopListening();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopListening()
|
||||||
|
{
|
||||||
|
_manager.StopUpdatingLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
using CoreLocation;
|
||||||
|
using eShopOnContainers.Core.Models.Location;
|
||||||
|
using eShopOnContainers.Core.Models.Permissions;
|
||||||
|
using eShopOnContainers.Core.Services.Location;
|
||||||
|
using eShopOnContainers.Core.Services.Permissions;
|
||||||
|
using eShopOnContainers.iOS.Services;
|
||||||
|
using Foundation;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UIKit;
|
||||||
|
|
||||||
|
[assembly: Xamarin.Forms.Dependency(typeof(LocationServiceImplementation))]
|
||||||
|
namespace eShopOnContainers.iOS.Services
|
||||||
|
{
|
||||||
|
public class LocationServiceImplementation : ILocationServiceImplementation
|
||||||
|
{
|
||||||
|
IPermissionsService _permissionsService;
|
||||||
|
|
||||||
|
public LocationServiceImplementation()
|
||||||
|
{
|
||||||
|
DesiredAccuracy = 100;
|
||||||
|
_permissionsService = new PermissionsService();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Internal Implementation
|
||||||
|
|
||||||
|
async Task<bool> CheckPermissions(Permission permission)
|
||||||
|
{
|
||||||
|
var status = await _permissionsService.CheckPermissionStatusAsync(permission);
|
||||||
|
if (status != PermissionStatus.Granted)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Currently do not have Location permissions, requesting permissions");
|
||||||
|
|
||||||
|
var request = await _permissionsService.RequestPermissionsAsync(permission);
|
||||||
|
if (request[permission] != PermissionStatus.Granted)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Location permission denied, can not get positions async.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLLocationManager GetManager()
|
||||||
|
{
|
||||||
|
CLLocationManager manager = null;
|
||||||
|
new NSObject().InvokeOnMainThread(() => manager = new CLLocationManager());
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ILocationServiceImplementation
|
||||||
|
|
||||||
|
public double DesiredAccuracy { get; set; }
|
||||||
|
public bool IsGeolocationAvailable => true;
|
||||||
|
public bool IsGeolocationEnabled
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var status = CLLocationManager.Status;
|
||||||
|
return CLLocationManager.LocationServicesEnabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Position> GetPositionAsync(TimeSpan? timeout, CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
var permission = Permission.LocationWhenInUse;
|
||||||
|
var hasPermission = await CheckPermissions(permission);
|
||||||
|
if (!hasPermission)
|
||||||
|
throw new GeolocationException(GeolocationError.Unauthorized);
|
||||||
|
|
||||||
|
var timeoutMilliseconds = timeout.HasValue ? (int)timeout.Value.TotalMilliseconds : Timeout.Infinite;
|
||||||
|
if (timeoutMilliseconds <= 0 && timeoutMilliseconds != Timeout.Infinite)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(timeout), "Timeout must be positive or Timeout.Infinite");
|
||||||
|
if (!cancelToken.HasValue)
|
||||||
|
cancelToken = CancellationToken.None;
|
||||||
|
|
||||||
|
TaskCompletionSource<Position> tcs;
|
||||||
|
|
||||||
|
var manager = GetManager();
|
||||||
|
manager.DesiredAccuracy = DesiredAccuracy;
|
||||||
|
|
||||||
|
// 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<Position>(manager);
|
||||||
|
var singleListener = new GeolocationSingleUpdateDelegate(manager, DesiredAccuracy, timeoutMilliseconds, cancelToken.Value);
|
||||||
|
manager.Delegate = singleListener;
|
||||||
|
manager.StartUpdatingLocation();
|
||||||
|
|
||||||
|
return await singleListener.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
using CoreLocation;
|
||||||
|
using eShopOnContainers.Core.Models.Permissions;
|
||||||
|
using eShopOnContainers.Core.Services.Permissions;
|
||||||
|
using Foundation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using UIKit;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.iOS.Services
|
||||||
|
{
|
||||||
|
public class PermissionsService : IPermissionsService
|
||||||
|
{
|
||||||
|
CLLocationManager _locationManager;
|
||||||
|
|
||||||
|
#region Internal Implementation
|
||||||
|
|
||||||
|
PermissionStatus GetLocationPermissionStatus(Permission permission)
|
||||||
|
{
|
||||||
|
if (!CLLocationManager.LocationServicesEnabled)
|
||||||
|
return PermissionStatus.Disabled;
|
||||||
|
|
||||||
|
var status = CLLocationManager.Status;
|
||||||
|
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case CLAuthorizationStatus.AuthorizedAlways:
|
||||||
|
case CLAuthorizationStatus.AuthorizedWhenInUse:
|
||||||
|
return PermissionStatus.Granted;
|
||||||
|
case CLAuthorizationStatus.Denied:
|
||||||
|
return PermissionStatus.Denied;
|
||||||
|
case CLAuthorizationStatus.Restricted:
|
||||||
|
return PermissionStatus.Restricted;
|
||||||
|
default:
|
||||||
|
return PermissionStatus.Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case CLAuthorizationStatus.Authorized:
|
||||||
|
return PermissionStatus.Granted;
|
||||||
|
case CLAuthorizationStatus.Denied:
|
||||||
|
return PermissionStatus.Denied;
|
||||||
|
case CLAuthorizationStatus.Restricted:
|
||||||
|
return PermissionStatus.Restricted;
|
||||||
|
default:
|
||||||
|
return PermissionStatus.Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Task<PermissionStatus> RequestLocationPermissionAsync(Permission permission = Permission.Location)
|
||||||
|
{
|
||||||
|
if (CLLocationManager.Status == CLAuthorizationStatus.AuthorizedWhenInUse && permission == Permission.LocationAlways)
|
||||||
|
{
|
||||||
|
// Don't do anything and request it
|
||||||
|
}
|
||||||
|
else if (GetLocationPermissionStatus(permission) != PermissionStatus.Unknown)
|
||||||
|
return Task.FromResult(GetLocationPermissionStatus(permission));
|
||||||
|
|
||||||
|
if (!UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
|
||||||
|
{
|
||||||
|
return Task.FromResult(PermissionStatus.Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandler<CLAuthorizationChangedEventArgs> authCallback = null;
|
||||||
|
var tcs = new TaskCompletionSource<PermissionStatus>();
|
||||||
|
_locationManager = new CLLocationManager();
|
||||||
|
|
||||||
|
authCallback = (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.Status == CLAuthorizationStatus.NotDetermined)
|
||||||
|
return;
|
||||||
|
_locationManager.AuthorizationChanged -= authCallback;
|
||||||
|
tcs.TrySetResult(GetLocationPermissionStatus(permission));
|
||||||
|
};
|
||||||
|
_locationManager.AuthorizationChanged += authCallback;
|
||||||
|
|
||||||
|
var info = NSBundle.MainBundle.InfoDictionary;
|
||||||
|
if (permission == Permission.LocationWhenInUse)
|
||||||
|
{
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IPermissionsServiceImplementation
|
||||||
|
|
||||||
|
public Task<PermissionStatus> CheckPermissionStatusAsync(Permission permission)
|
||||||
|
{
|
||||||
|
switch (permission)
|
||||||
|
{
|
||||||
|
case Permission.LocationWhenInUse:
|
||||||
|
return Task.FromResult(GetLocationPermissionStatus(permission));
|
||||||
|
}
|
||||||
|
return Task.FromResult(PermissionStatus.Granted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<Permission, PermissionStatus>> RequestPermissionsAsync(params Permission[] permissions)
|
||||||
|
{
|
||||||
|
var results = new Dictionary<Permission, PermissionStatus>();
|
||||||
|
foreach (var permission in permissions)
|
||||||
|
{
|
||||||
|
if (results.ContainsKey(permission))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (permission)
|
||||||
|
{
|
||||||
|
case Permission.LocationWhenInUse:
|
||||||
|
results.Add(permission, await RequestLocationPermissionAsync(permission).ConfigureAwait(false));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!results.ContainsKey(permission))
|
||||||
|
results.Add(permission, PermissionStatus.Granted);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -126,6 +126,9 @@
|
|||||||
<BundleResource Include="Resources\menu_campaigns%403x.png" />
|
<BundleResource Include="Resources\menu_campaigns%403x.png" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
||||||
|
<Compile Include="Services\LocationServiceImplementation.cs" />
|
||||||
|
<Compile Include="Services\PermissionsService.cs" />
|
||||||
|
<Compile Include="Services\GeolocationSingleUpdateDelegate.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BundleResource Include="Resources\Icon-60%403x.png" />
|
<BundleResource Include="Resources\Icon-60%403x.png" />
|
||||||
@ -198,12 +201,6 @@
|
|||||||
<Reference Include="Acr.UserDialogs.Interface">
|
<Reference Include="Acr.UserDialogs.Interface">
|
||||||
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.Interface.dll</HintPath>
|
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.Interface.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Plugin.Geolocator.Abstractions">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\Xamarin.iOS10\Plugin.Geolocator.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Plugin.Geolocator">
|
|
||||||
<HintPath>..\..\..\..\packages\Xam.Plugin.Geolocator.3.0.4\lib\Xamarin.iOS10\Plugin.Geolocator.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="PInvoke.Windows.Core">
|
<Reference Include="PInvoke.Windows.Core">
|
||||||
<HintPath>..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
|
<HintPath>..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="xamarinios10" />
|
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="xamarinios10" />
|
||||||
<package id="Validation" version="2.2.8" targetFramework="xamarinios10" />
|
<package id="Validation" version="2.2.8" targetFramework="xamarinios10" />
|
||||||
<package id="WebP.Touch" version="1.0.7" targetFramework="xamarinios10" />
|
<package id="WebP.Touch" version="1.0.7" targetFramework="xamarinios10" />
|
||||||
<package id="Xam.Plugin.Geolocator" version="3.0.4" targetFramework="xamarinios10" />
|
|
||||||
<package id="Xamarin.FFImageLoading" version="2.3.4" targetFramework="xamarinios10" />
|
<package id="Xamarin.FFImageLoading" version="2.3.4" targetFramework="xamarinios10" />
|
||||||
<package id="Xamarin.FFImageLoading.Forms" version="2.3.4" targetFramework="xamarinios10" />
|
<package id="Xamarin.FFImageLoading.Forms" version="2.3.4" targetFramework="xamarinios10" />
|
||||||
<package id="Xamarin.Forms" version="2.5.0.122203" targetFramework="xamarinios10" />
|
<package id="Xamarin.Forms" version="2.5.0.122203" targetFramework="xamarinios10" />
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -30,7 +30,8 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even
|
|||||||
|
|
||||||
private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, decimal oldPrice, CustomerBasket basket)
|
private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, decimal oldPrice, CustomerBasket basket)
|
||||||
{
|
{
|
||||||
var itemsToUpdate = basket?.Items?.Where(x => int.Parse(x.ProductId) == productId).ToList();
|
string match = productId.ToString();
|
||||||
|
var itemsToUpdate = basket?.Items?.Where(x => x.ProductId == match).ToList();
|
||||||
|
|
||||||
if (itemsToUpdate != null)
|
if (itemsToUpdate != null)
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -39,7 +39,7 @@ namespace Catalog.API.Infrastructure.Filters
|
|||||||
{
|
{
|
||||||
var json = new JsonErrorResponse
|
var json = new JsonErrorResponse
|
||||||
{
|
{
|
||||||
Messages = new[] { "An error ocurr.Try it again." }
|
Messages = new[] { "An error ocurred." }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
|
@ -33,7 +33,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
|
|||||||
|
|
||||||
if (!context.Users.Any())
|
if (!context.Users.Any())
|
||||||
{
|
{
|
||||||
await context.Users.AddRangeAsync(useCustomizationData
|
context.Users.AddRange(useCustomizationData
|
||||||
? GetUsersFromFile(contentRootPath, logger)
|
? GetUsersFromFile(contentRootPath, logger)
|
||||||
: GetDefaultUser());
|
: GetDefaultUser());
|
||||||
|
|
||||||
|
@ -27,17 +27,17 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
|
|||||||
|
|
||||||
if (!await context.Clients.AnyAsync())
|
if (!await context.Clients.AnyAsync())
|
||||||
{
|
{
|
||||||
await context.Clients.AddRangeAsync(Config.GetClients(clientUrls).Select(client => client.ToEntity()));
|
context.Clients.AddRange(Config.GetClients(clientUrls).Select(client => client.ToEntity()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await context.IdentityResources.AnyAsync())
|
if (!await context.IdentityResources.AnyAsync())
|
||||||
{
|
{
|
||||||
await context.IdentityResources.AddRangeAsync(Config.GetResources().Select(resource => resource.ToEntity()));
|
context.IdentityResources.AddRange(Config.GetResources().Select(resource => resource.ToEntity()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await context.ApiResources.AnyAsync())
|
if (!await context.ApiResources.AnyAsync())
|
||||||
{
|
{
|
||||||
await context.ApiResources.AddRangeAsync(Config.GetApis().Select(api => api.ToEntity()));
|
context.ApiResources.AddRange(Config.GetApis().Select(api => api.ToEntity()));
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
if (!context.CardTypes.Any())
|
if (!context.CardTypes.Any())
|
||||||
{
|
{
|
||||||
await context.CardTypes.AddRangeAsync(useCustomizationData
|
context.CardTypes.AddRange(useCustomizationData
|
||||||
? GetCardTypesFromFile(contentRootPath, logger)
|
? GetCardTypesFromFile(contentRootPath, logger)
|
||||||
: GetPredefinedCardTypes());
|
: GetPredefinedCardTypes());
|
||||||
|
|
||||||
@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
if (!context.OrderStatus.Any())
|
if (!context.OrderStatus.Any())
|
||||||
{
|
{
|
||||||
await context.OrderStatus.AddRangeAsync(useCustomizationData
|
context.OrderStatus.AddRange(useCustomizationData
|
||||||
? GetOrderStatusFromFile(contentRootPath, logger)
|
? GetOrderStatusFromFile(contentRootPath, logger)
|
||||||
: GetPredefinedOrderStatus());
|
: GetPredefinedOrderStatus());
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@ -11,9 +11,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
|||||||
|
|
||||||
public int Id { get; private set; }
|
public int Id { get; private set; }
|
||||||
|
|
||||||
protected Enumeration()
|
protected Enumeration(){}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Enumeration(int id, string name)
|
protected Enumeration(int id, string name)
|
||||||
{
|
{
|
||||||
@ -21,10 +19,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
|||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString() => Name;
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
|
public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
|
||||||
{
|
{
|
||||||
@ -37,20 +32,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
|||||||
var locatedValue = info.GetValue(instance) as T;
|
var locatedValue = info.GetValue(instance) as T;
|
||||||
|
|
||||||
if (locatedValue != null)
|
if (locatedValue != null)
|
||||||
{
|
|
||||||
yield return locatedValue;
|
yield return locatedValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
var otherValue = obj as Enumeration;
|
var otherValue = obj as Enumeration;
|
||||||
|
|
||||||
if (otherValue == null)
|
if (otherValue == null)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
var typeMatches = GetType().Equals(obj.GetType());
|
var typeMatches = GetType().Equals(obj.GetType());
|
||||||
var valueMatches = Id.Equals(otherValue.Id);
|
var valueMatches = Id.Equals(otherValue.Id);
|
||||||
@ -58,10 +49,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
|||||||
return typeMatches && valueMatches;
|
return typeMatches && valueMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode() => Id.GetHashCode();
|
||||||
{
|
|
||||||
return Id.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
|
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
|
||||||
{
|
{
|
||||||
@ -86,18 +74,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
|||||||
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
|
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
|
||||||
|
|
||||||
if (matchingItem == null)
|
if (matchingItem == null)
|
||||||
{
|
throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}");
|
||||||
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
|
|
||||||
|
|
||||||
throw new InvalidOperationException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchingItem;
|
return matchingItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CompareTo(object other)
|
public int CompareTo(object other) => Id.CompareTo(((Enumeration) other).Id);
|
||||||
{
|
|
||||||
return Id.CompareTo(((Enumeration)other).Id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
|
@ -2,7 +2,7 @@ FROM microsoft/aspnetcore:2.0.3 AS base
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
FROM microsoft/aspnetcore-build:2.0 AS build
|
FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet restore -nowarn:msb3202,nu1503
|
RUN dotnet restore -nowarn:msb3202,nu1503
|
||||||
@ -16,3 +16,4 @@ FROM base AS final
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=publish /app .
|
COPY --from=publish /app .
|
||||||
ENTRYPOINT ["dotnet", "WebStatus.dll"]
|
ENTRYPOINT ["dotnet", "WebStatus.dll"]
|
||||||
|
|
||||||
|
@ -58,6 +58,15 @@ namespace IntegrationTests.Services.Basket
|
|||||||
string BuildBasket()
|
string BuildBasket()
|
||||||
{
|
{
|
||||||
var order = new CustomerBasket("1234");
|
var order = new CustomerBasket("1234");
|
||||||
|
|
||||||
|
order.Items.Add(new BasketItem
|
||||||
|
{
|
||||||
|
ProductId = "1",
|
||||||
|
ProductName = ".NET Bot Black Hoodie",
|
||||||
|
UnitPrice = 10,
|
||||||
|
Quantity = 1
|
||||||
|
});
|
||||||
|
|
||||||
return JsonConvert.SerializeObject(order);
|
return JsonConvert.SerializeObject(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +79,10 @@ namespace IntegrationTests.Services.Basket
|
|||||||
State = "state",
|
State = "state",
|
||||||
Country = "coutry",
|
Country = "coutry",
|
||||||
ZipCode = "zipcode",
|
ZipCode = "zipcode",
|
||||||
CardNumber = "CardNumber",
|
CardNumber = "1234567890123456",
|
||||||
CardHolderName = "CardHolderName",
|
CardHolderName = "CardHolderName",
|
||||||
CardExpiration = DateTime.UtcNow,
|
CardExpiration = DateTime.UtcNow.AddDays(1),
|
||||||
CardSecurityNumber = "1234",
|
CardSecurityNumber = "123",
|
||||||
CardTypeId = 1,
|
CardTypeId = 1,
|
||||||
Buyer = "Buyer",
|
Buyer = "Buyer",
|
||||||
RequestId = Guid.NewGuid()
|
RequestId = Guid.NewGuid()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user