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 EnsurePermissionAsync() { var status = await Permissions.CheckStatusAsync(); if (status != PermissionStatus.Granted) { status = await Permissions.RequestAsync(); } return status; } public async Task 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 onLocation, Func 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);