Work in progress.
This commit is contained in:
parent
68d0783070
commit
41398ab03a
@ -3,11 +3,11 @@ using eShopOnContainers.Core.Services.Location;
|
||||
using eShopOnContainers.Core.Services.Settings;
|
||||
using eShopOnContainers.Core.ViewModels.Base;
|
||||
using eShopOnContainers.Services;
|
||||
using Plugin.Geolocator;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
using eShopOnContainers.Core.Services.Dependency;
|
||||
|
||||
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
namespace eShopOnContainers
|
||||
@ -69,13 +69,16 @@ namespace eShopOnContainers
|
||||
|
||||
private async Task GetGpsLocation()
|
||||
{
|
||||
var locator = CrossGeolocator.Current;
|
||||
var dependencyService = ViewModelLocator.Resolve<IDependencyService>();
|
||||
var locator = dependencyService.Get<ILocationServiceImplementation>();
|
||||
|
||||
if (locator.IsGeolocationEnabled && locator.IsGeolocationAvailable)
|
||||
{
|
||||
locator.AllowsBackgroundUpdates = true;
|
||||
//locationService.AllowsBackgroundUpdates = true;
|
||||
locator.DesiredAccuracy = 50;
|
||||
|
||||
await Task.Delay(5000);
|
||||
|
||||
var position = await locator.GetPositionAsync();
|
||||
|
||||
_settingsService.Latitude = position.Latitude.ToString();
|
||||
|
@ -0,0 +1,10 @@
|
||||
namespace eShopOnContainers.Core.Models.Location
|
||||
{
|
||||
public enum ActivityType
|
||||
{
|
||||
Other,
|
||||
AutomotiveNavigation,
|
||||
Fitness,
|
||||
OtherNavigation
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace eShopOnContainers.Core.Models.Location
|
||||
{
|
||||
public class Address
|
||||
{
|
||||
public double Latitude { get; set; }
|
||||
public double Longitude { get; set; }
|
||||
public string CountryCode { get; set; }
|
||||
public string CountryName { get; set; }
|
||||
public string FeatureName { get; set; }
|
||||
public string PostalCode { get; set; }
|
||||
public string SubLocality { get; set; }
|
||||
public string Thoroughfare { get; set; }
|
||||
public string SubThoroughfare { get; set; }
|
||||
public string Locality { get; set; }
|
||||
public string AdminArea { get; set; }
|
||||
public string SubAdminArea { get; set; }
|
||||
|
||||
public Address(Address address)
|
||||
{
|
||||
if (address == null)
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
|
||||
CountryCode = address.CountryCode;
|
||||
CountryName = address.CountryName;
|
||||
Latitude = address.Latitude;
|
||||
Longitude = address.Longitude;
|
||||
FeatureName = address.FeatureName;
|
||||
PostalCode = address.PostalCode;
|
||||
SubLocality = address.SubLocality;
|
||||
Thoroughfare = address.Thoroughfare;
|
||||
SubThoroughfare = address.SubThoroughfare;
|
||||
SubAdminArea = address.SubAdminArea;
|
||||
AdminArea = address.AdminArea;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,15 @@
|
||||
using System;
|
||||
|
||||
namespace eShopOnContainers.Core.Models.Location
|
||||
{
|
||||
public class ListenerSettings
|
||||
{
|
||||
public bool AllowBackgroundUpdates { get; set; } = false;
|
||||
public bool PauseLocationUpdatesAutomatically { get; set; } = true;
|
||||
public ActivityType ActivityType { get; set; } = ActivityType.Other;
|
||||
public bool ListenForSignificantChanges { get; set; } = false;
|
||||
public bool DeferLocationUpdates { get; set; } = false;
|
||||
public double? DeferralDistanceMeters { get; set; } = 500;
|
||||
public TimeSpan? DeferralTime { get; set; } = TimeSpan.FromMinutes(5);
|
||||
}
|
||||
}
|
@ -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,14 @@
|
||||
using System;
|
||||
|
||||
namespace eShopOnContainers.Core.Models.Location
|
||||
{
|
||||
public class PositionErrorEventArgs : EventArgs
|
||||
{
|
||||
public GeolocationError Error { get; private set; }
|
||||
|
||||
public PositionErrorEventArgs(GeolocationError error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace eShopOnContainers.Core.Models.Location
|
||||
{
|
||||
public class PositionEventArgs : EventArgs
|
||||
{
|
||||
public Position Position { get; private set; }
|
||||
|
||||
public PositionEventArgs(Position position)
|
||||
{
|
||||
if (position == null)
|
||||
throw new ArgumentNullException("position");
|
||||
|
||||
Position = position;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using eShopOnContainers.Core.Models.Location;
|
||||
|
||||
namespace eShopOnContainers.Core.Services.Location
|
||||
{
|
||||
public interface ILocationServiceImplementation
|
||||
{
|
||||
event EventHandler<PositionErrorEventArgs> PositionError;
|
||||
event EventHandler<PositionEventArgs> PositionChanged;
|
||||
|
||||
double DesiredAccuracy { get; set; }
|
||||
bool IsGeolocationAvailable { get; }
|
||||
bool IsGeolocationEnabled { get; }
|
||||
|
||||
Task<Position> GetPositionAsync(TimeSpan? timeout = null, CancellationToken? token = null, bool includeHeading = false);
|
||||
}
|
||||
}
|
@ -16,11 +16,8 @@ namespace eShopOnContainers.Core.Services.Location
|
||||
public async Task UpdateUserLocation(eShopOnContainers.Core.Models.Location.Location newLocReq, string token)
|
||||
{
|
||||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.LocationEndpoint);
|
||||
|
||||
builder.Path = "api/v1/locations";
|
||||
|
||||
string uri = builder.ToString();
|
||||
|
||||
await _requestProvider.PostAsync(uri, newLocReq, 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);
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@ using eShopOnContainers.Core.Models.User;
|
||||
using eShopOnContainers.Core.Services.Location;
|
||||
using eShopOnContainers.Core.Services.Settings;
|
||||
using eShopOnContainers.Core.ViewModels.Base;
|
||||
using Plugin.Geolocator;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Xamarin.Forms;
|
||||
using eShopOnContainers.Core.Services.Dependency;
|
||||
|
||||
namespace eShopOnContainers.Core.ViewModels
|
||||
{
|
||||
@ -29,11 +29,13 @@ namespace eShopOnContainers.Core.ViewModels
|
||||
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ILocationService _locationService;
|
||||
private readonly IDependencyService _dependencyService;
|
||||
|
||||
public SettingsViewModel(ISettingsService settingsService, ILocationService locationService)
|
||||
public SettingsViewModel(ISettingsService settingsService, ILocationService locationService, IDependencyService dependencyService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_locationService = locationService;
|
||||
_dependencyService = dependencyService;
|
||||
|
||||
_useAzureServices = !_settingsService.UseMocks;
|
||||
_endpoint = _settingsService.UrlBase;
|
||||
@ -342,7 +344,7 @@ namespace eShopOnContainers.Core.ViewModels
|
||||
{
|
||||
if (_allowGpsLocation)
|
||||
{
|
||||
var locator = CrossGeolocator.Current;
|
||||
var locator = _dependencyService.Get<ILocationServiceImplementation>();
|
||||
if (!locator.IsGeolocationEnabled)
|
||||
{
|
||||
_allowGpsLocation = false;
|
||||
|
@ -10,9 +10,12 @@
|
||||
<PackageReference Include="SlideOverKit" Version="2.1.5" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<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.Forms" Version="2.5.0.122203" />
|
||||
<PackageReference Include="IdentityModel" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Services\Permissions\" />
|
||||
<Folder Include="Models\Permissions\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -57,6 +57,7 @@ namespace eShopOnContainers.Droid.Activities
|
||||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
|
||||
{
|
||||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Plugin.CurrentActivity;
|
||||
using System;
|
||||
|
||||
namespace eShopOnContainers.Droid
|
||||
@ -9,6 +9,8 @@ namespace eShopOnContainers.Droid
|
||||
[Application]
|
||||
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
|
||||
{
|
||||
internal static Context CurrentContext { get; private set; }
|
||||
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership transer)
|
||||
: base(handle, transer)
|
||||
{
|
||||
@ -28,7 +30,7 @@ namespace eShopOnContainers.Droid
|
||||
|
||||
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
|
||||
{
|
||||
CrossCurrentActivity.Current.Activity = activity;
|
||||
CurrentContext = activity;
|
||||
}
|
||||
|
||||
public void OnActivityDestroyed(Activity activity)
|
||||
@ -41,7 +43,7 @@ namespace eShopOnContainers.Droid
|
||||
|
||||
public void OnActivityResumed(Activity activity)
|
||||
{
|
||||
CrossCurrentActivity.Current.Activity = activity;
|
||||
CurrentContext = activity;
|
||||
}
|
||||
|
||||
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
|
||||
@ -50,7 +52,7 @@ namespace eShopOnContainers.Droid
|
||||
|
||||
public void OnActivityStarted(Activity activity)
|
||||
{
|
||||
CrossCurrentActivity.Current.Activity = activity;
|
||||
CurrentContext = activity;
|
||||
}
|
||||
|
||||
public void OnActivityStopped(Activity activity)
|
||||
|
@ -0,0 +1,241 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using eShopOnContainers.Core.Models.Permissions;
|
||||
using eShopOnContainers.Core.Services.Permissions;
|
||||
using System.Linq;
|
||||
using Android;
|
||||
using Android.App;
|
||||
using Android.Support.V4.App;
|
||||
using Android.Support.V4.Content;
|
||||
using System.Diagnostics;
|
||||
using Android.Icu.Text;
|
||||
|
||||
namespace eShopOnContainers.Droid.Services
|
||||
{
|
||||
public class PermissionService : IPermissionsService
|
||||
{
|
||||
const int _permissionCode = 25;
|
||||
object _locker = new object();
|
||||
TaskCompletionSource<Dictionary<Permission, PermissionStatus>> _tcs;
|
||||
Dictionary<Permission, PermissionStatus> _results;
|
||||
IList<string> _requestedPermissions;
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
static 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">
|
||||
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\MonoAndroid10\Acr.UserDialogs.Interface.dll</HintPath>
|
||||
</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">
|
||||
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\MonoAndroid10\SlideOverKit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SlideOverKit.Droid">
|
||||
<HintPath>..\..\..\..\packages\SlideOverKit.2.1.5\lib\MonoAndroid10\SlideOverKit.Droid.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="System.Net.Http.Extensions">
|
||||
<HintPath>..\..\..\..\packages\Microsoft.Net.Http.2.2.28\lib\monoandroid\System.Net.Http.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
@ -227,6 +212,7 @@
|
||||
<Compile Include="Effects\BaseContainerEffect.cs" />
|
||||
<Compile Include="Activities\SplashActivity.cs" />
|
||||
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
||||
<Compile Include="Services\PermissionService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="..\CommonResources\Fonts\Montserrat-Bold.ttf">
|
||||
|
@ -17,8 +17,6 @@
|
||||
<package id="PInvoke.Kernel32" 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="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="Splat" version="2.0.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.XmlDocument" version="4.3.0" 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.Annotations" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||
<package id="Xamarin.Android.Support.Compat" version="25.4.0.2" targetFramework="monoandroid80" />
|
||||
|
@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using CoreLocation;
|
||||
using Foundation;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using eShopOnContainers.Core.Models.Location;
|
||||
|
||||
namespace eShopOnContainers.iOS.Services
|
||||
{
|
||||
internal class GeolocationSingleUpdateDelegate : CLLocationManagerDelegate
|
||||
{
|
||||
bool _haveHeading;
|
||||
bool _haveLocation;
|
||||
readonly Position _position = new Position();
|
||||
CLHeading _bestHeading;
|
||||
|
||||
readonly double _desiredAccuracy;
|
||||
readonly bool _includeHeading;
|
||||
readonly TaskCompletionSource<Position> _tcs;
|
||||
readonly CLLocationManager _manager;
|
||||
|
||||
public Task<Position> Task => _tcs?.Task;
|
||||
|
||||
public GeolocationSingleUpdateDelegate(CLLocationManager manager, double desiredAccuracy, bool includeHeading, int timeout, CancellationToken cancelToken)
|
||||
{
|
||||
_manager = manager;
|
||||
_tcs = new TaskCompletionSource<Position>(manager);
|
||||
_desiredAccuracy = desiredAccuracy;
|
||||
_includeHeading = includeHeading;
|
||||
|
||||
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 bool ShouldDisplayHeadingCalibration(CLLocationManager manager) => true;
|
||||
|
||||
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 ex)
|
||||
{
|
||||
_position.Timestamp = DateTimeOffset.UtcNow;
|
||||
}
|
||||
_haveLocation = true;
|
||||
|
||||
if ((!_includeHeading || _haveHeading) && _position.Accuracy <= _desiredAccuracy)
|
||||
{
|
||||
_tcs.TrySetResult(new Position(_position));
|
||||
StopListening();
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdatedHeading(CLLocationManager manager, CLHeading newHeading)
|
||||
{
|
||||
if (newHeading.HeadingAccuracy < 0)
|
||||
return;
|
||||
if (_bestHeading != null && newHeading.HeadingAccuracy >= _bestHeading.HeadingAccuracy)
|
||||
return;
|
||||
|
||||
_bestHeading = newHeading;
|
||||
_position.Heading = newHeading.TrueHeading;
|
||||
_haveHeading = true;
|
||||
|
||||
if (_haveLocation && _position.Accuracy <= _desiredAccuracy)
|
||||
{
|
||||
_tcs.TrySetResult(new Position(_position));
|
||||
StopListening();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopListening()
|
||||
{
|
||||
if (CLLocationManager.HeadingAvailable)
|
||||
_manager.StopUpdatingHeading();
|
||||
|
||||
_manager.StopUpdatingLocation();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
using eShopOnContainers.iOS.Services;
|
||||
using eShopOnContainers.Core.Services.Location;
|
||||
using CoreLocation;
|
||||
using eShopOnContainers.Core.Models.Location;
|
||||
using Foundation;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using UIKit;
|
||||
using eShopOnContainers.Core.Models.Permissions;
|
||||
using eShopOnContainers.Core.Services.Permissions;
|
||||
|
||||
[assembly: Xamarin.Forms.Dependency(typeof(LocationServiceImplementation))]
|
||||
namespace eShopOnContainers.iOS.Services
|
||||
{
|
||||
public class LocationServiceImplementation : ILocationServiceImplementation
|
||||
{
|
||||
bool _deferringUpdates;
|
||||
readonly CLLocationManager _manager;
|
||||
Position _lastPosition;
|
||||
|
||||
public event EventHandler<PositionErrorEventArgs> PositionError;
|
||||
public event EventHandler<PositionEventArgs> PositionChanged;
|
||||
public double DesiredAccuracy { get; set; }
|
||||
public bool IsGeolocationAvailable => true;
|
||||
public bool IsGeolocationEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
var status = CLLocationManager.Status;
|
||||
return CLLocationManager.LocationServicesEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SupportsHeading => CLLocationManager.HeadingAvailable;
|
||||
|
||||
public LocationServiceImplementation()
|
||||
{
|
||||
DesiredAccuracy = 100;
|
||||
//_manager = GetManager();
|
||||
//_manager.AuthorizationChanged += OnAuthorizationChanged;
|
||||
//_manager.Failed += OnFailed;
|
||||
//_manager.UpdatedLocation += OnUpdatedLocation;
|
||||
//_manager.UpdatedHeading += OnUpdatedHeading;
|
||||
//_manager.DeferredUpdatesFinished += OnDeferredUpdatesFinished;
|
||||
}
|
||||
|
||||
void OnDeferredUpdatesFinished(object sender, NSErrorEventArgs e) => _deferringUpdates = false;
|
||||
|
||||
#region Internal Implementation
|
||||
|
||||
async Task<bool> CheckPermissions(Permission permission)
|
||||
{
|
||||
IPermissionsService permissionsService = new PermissionsService();
|
||||
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 async Task<Position> GetPositionAsync(TimeSpan? timeout, CancellationToken? cancelToken = null, bool includeHeading = false)
|
||||
{
|
||||
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;
|
||||
|
||||
// Permit background updates if background location mode is enabled
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0))
|
||||
{
|
||||
var backgroundModes = NSBundle.MainBundle.InfoDictionary[(NSString)"UIBackgroundModes"] as NSArray;
|
||||
manager.AllowsBackgroundLocationUpdates = backgroundModes != null && (backgroundModes.Contains((NSString)"Location") || backgroundModes.Contains((NSString)"location"));
|
||||
}
|
||||
|
||||
// Always prevent location update pausing since we're only listening for a single update
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0))
|
||||
manager.PausesLocationUpdatesAutomatically = false;
|
||||
|
||||
tcs = new TaskCompletionSource<Position>(manager);
|
||||
var singleListener = new GeolocationSingleUpdateDelegate(manager, DesiredAccuracy, includeHeading, timeoutMilliseconds, cancelToken.Value);
|
||||
manager.Delegate = singleListener;
|
||||
manager.StartUpdatingLocation();
|
||||
|
||||
if (includeHeading && SupportsHeading)
|
||||
manager.StartUpdatingHeading();
|
||||
|
||||
return await singleListener.Task;
|
||||
|
||||
//tcs = new TaskCompletionSource<Position>();
|
||||
//if (_lastPosition == null)
|
||||
//{
|
||||
// if (cancelToken != CancellationToken.None)
|
||||
// cancelToken.Value.Register(() => tcs.TrySetCanceled());
|
||||
|
||||
// EventHandler<PositionErrorEventArgs> gotError = null;
|
||||
// gotError = (s, e) =>
|
||||
// {
|
||||
// tcs.TrySetException(new GeolocationException(e.Error));
|
||||
// PositionError -= gotError;
|
||||
// };
|
||||
// PositionError += gotError;
|
||||
|
||||
// EventHandler<PositionEventArgs> gotPosition = null;
|
||||
// gotPosition = (s, e) =>
|
||||
// {
|
||||
// tcs.TrySetResult(e.Position);
|
||||
// PositionChanged += gotPosition;
|
||||
// };
|
||||
// PositionChanged += gotPosition;
|
||||
//}
|
||||
//else
|
||||
// tcs.SetResult(_lastPosition);
|
||||
|
||||
//return await tcs.Task;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using eShopOnContainers.Core.Models.Permissions;
|
||||
using eShopOnContainers.Core.Services.Permissions;
|
||||
using CoreLocation;
|
||||
using UIKit;
|
||||
using Foundation;
|
||||
|
||||
namespace eShopOnContainers.iOS.Services
|
||||
{
|
||||
public class PermissionsService : IPermissionsService
|
||||
{
|
||||
#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>();
|
||||
var 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.Location)
|
||||
//{
|
||||
// if (info.ContainsKey(new NSString("NSLocationAlwaysUsageDescription")))
|
||||
// locationManager.RequestAlwaysAuthorization();
|
||||
// else if (info.ContainsKey(new NSString("NSLocationWhenInUseUsageDescription")))
|
||||
// locationManager.RequestWhenInUseAuthorization();
|
||||
// else
|
||||
// throw new UnauthorizedAccessException("On iOS 8.0 and higher you must set either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in your Info.plist file to enable Authorization Requests for Location updates!");
|
||||
//}
|
||||
//else if (permission == Permission.LocationAlways)
|
||||
//{
|
||||
// if (info.ContainsKey(new NSString("NSLocationAlwaysUsageDescription")))
|
||||
// locationManager.RequestAlwaysAuthorization();
|
||||
// else
|
||||
// throw new UnauthorizedAccessException("On iOS 8.0 and higher you must set either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in your Info.plist file to enable Authorization Requests for Location updates!");
|
||||
//}
|
||||
if (permission == Permission.LocationWhenInUse)
|
||||
{
|
||||
if (info.ContainsKey(new NSString("NSLocationWhenInUseUsageDescription")))
|
||||
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" />
|
||||
<None Include="packages.config" />
|
||||
<Compile Include="Services\SettingsServiceImplementation.cs" />
|
||||
<Compile Include="Services\LocationServiceImplementation.cs" />
|
||||
<Compile Include="Services\PermissionsService.cs" />
|
||||
<Compile Include="Services\GeolocationSingleUpdateDelegate.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="Resources\Icon-60%403x.png" />
|
||||
@ -198,12 +201,6 @@
|
||||
<Reference Include="Acr.UserDialogs.Interface">
|
||||
<HintPath>..\..\..\..\packages\Acr.UserDialogs.6.5.1\lib\Xamarin.iOS10\Acr.UserDialogs.Interface.dll</HintPath>
|
||||
</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">
|
||||
<HintPath>..\..\..\..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -65,7 +65,6 @@
|
||||
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="xamarinios10" />
|
||||
<package id="Validation" version="2.2.8" 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.Forms" version="2.3.4" targetFramework="xamarinios10" />
|
||||
<package id="Xamarin.Forms" version="2.5.0.122203" targetFramework="xamarinios10" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user