130 lines
3.8 KiB
C#
130 lines
3.8 KiB
C#
using Microsoft.Maui.ApplicationModel;
|
|
using Microsoft.Maui.Devices.Sensors;
|
|
|
|
namespace LMEMappeCatastaliApp.Services;
|
|
|
|
public sealed class PreciseLocationService : IAsyncDisposable
|
|
{
|
|
private CancellationTokenSource? trackingCancellation;
|
|
|
|
public bool IsTracking => trackingCancellation is not null;
|
|
|
|
public async Task<PermissionStatus> EnsurePermissionAsync()
|
|
{
|
|
var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
|
|
if (status != PermissionStatus.Granted)
|
|
{
|
|
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
public async Task<LocationSnapshot?> GetCurrentLocationAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
var permission = await EnsurePermissionAsync();
|
|
if (permission != PermissionStatus.Granted)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Location? location = null;
|
|
try
|
|
{
|
|
var request = new GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(8));
|
|
location = await Geolocation.Default.GetLocationAsync(request, cancellationToken);
|
|
}
|
|
catch (FeatureNotSupportedException)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (location is null)
|
|
{
|
|
var lastKnown = await Geolocation.Default.GetLastKnownLocationAsync();
|
|
if (lastKnown is not null && lastKnown.Timestamp >= DateTimeOffset.UtcNow.AddMinutes(-15))
|
|
{
|
|
location = lastKnown;
|
|
}
|
|
}
|
|
|
|
return location is null ? null : ToSnapshot(location);
|
|
}
|
|
|
|
public async Task StartTrackingAsync(Func<LocationSnapshot, Task> onLocation, Func<string, Task> onError)
|
|
{
|
|
if (trackingCancellation is not null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var permission = await EnsurePermissionAsync();
|
|
if (permission != PermissionStatus.Granted)
|
|
{
|
|
await onError("Permesso GPS non concesso.");
|
|
return;
|
|
}
|
|
|
|
trackingCancellation = new CancellationTokenSource();
|
|
var token = trackingCancellation.Token;
|
|
|
|
_ = Task.Run(async () =>
|
|
{
|
|
while (!token.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
var request = new GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(5));
|
|
var location = await Geolocation.Default.GetLocationAsync(request, token);
|
|
if (location is not null)
|
|
{
|
|
await onLocation(ToSnapshot(location));
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
break;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await onError(ex.Message);
|
|
await Task.Delay(TimeSpan.FromSeconds(2), token);
|
|
}
|
|
|
|
await Task.Delay(TimeSpan.FromSeconds(1), token);
|
|
}
|
|
}, token);
|
|
}
|
|
|
|
public void StopTracking()
|
|
{
|
|
trackingCancellation?.Cancel();
|
|
trackingCancellation?.Dispose();
|
|
trackingCancellation = null;
|
|
}
|
|
|
|
public ValueTask DisposeAsync()
|
|
{
|
|
StopTracking();
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
private static LocationSnapshot ToSnapshot(Location location) => new(
|
|
location.Latitude,
|
|
location.Longitude,
|
|
location.Accuracy,
|
|
location.Altitude,
|
|
location.Course,
|
|
location.Speed,
|
|
location.Timestamp);
|
|
}
|
|
|
|
public sealed record LocationSnapshot(
|
|
double Latitude,
|
|
double Longitude,
|
|
double? Accuracy,
|
|
double? Altitude,
|
|
double? Course,
|
|
double? Speed,
|
|
DateTimeOffset Timestamp);
|