diff --git a/src/Essentials/src/Geolocation/Geolocation.android.cs b/src/Essentials/src/Geolocation/Geolocation.android.cs index b7035227b163..e26925a01b37 100644 --- a/src/Essentials/src/Geolocation/Geolocation.android.cs +++ b/src/Essentials/src/Geolocation/Geolocation.android.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Linq; @@ -18,40 +19,46 @@ partial class GeolocationImplementation : IGeolocation const long twoMinutes = 120000; static readonly string[] ignoredProviders = new string[] { LocationManager.PassiveProvider, "local_database" }; - static ContinuousLocationListener continuousListener; - static List listeningProviders; + static ContinuousLocationListener? continuousListener; + static List? listeningProviders; - static LocationManager locationManager; + static LocationManager? locationManager; - static LocationManager LocationManager => + static LocationManager? LocationManager => locationManager ??= Application.Context.GetSystemService(Context.LocationService) as LocationManager; /// /// Indicates if currently listening to location updates while the app is in foreground. /// - public bool IsListeningForeground { get => continuousListener != null; } + public bool IsListeningForeground { get => continuousListener is not null; } - public async Task GetLastKnownLocationAsync() + public async Task GetLastKnownLocationAsync() { + if (LocationManager is null) + throw new FeatureNotSupportedException("Android LocationManager is not available"); + await Permissions.EnsureGrantedOrRestrictedAsync(); - AndroidLocation bestLocation = null; + AndroidLocation? bestLocation = null; foreach (var provider in LocationManager.GetProviders(true)) { var location = LocationManager.GetLastKnownLocation(provider); - if (location != null && IsBetterLocation(location, bestLocation)) + if (location is not null && IsBetterLocation(location, bestLocation)) bestLocation = location; } return bestLocation?.ToLocation(); } - public async Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) + public async Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(request); + if (LocationManager is null) + throw new FeatureNotSupportedException("Android LocationManager is not available"); + await Permissions.EnsureGrantedOrRestrictedAsync(); var enabledProviders = LocationManager.GetProviders(true); @@ -68,7 +75,7 @@ public async Task GetLocationAsync(GeolocationRequest request, Cancell if (string.IsNullOrEmpty(providerInfo.Provider)) return await GetLastKnownLocationAsync(); - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); var allProviders = LocationManager.GetProviders(false); @@ -96,7 +103,7 @@ public async Task GetLocationAsync(GeolocationRequest request, Cancell var androidLocation = await tcs.Task; - if (androidLocation == null) + if (androidLocation is null) return null; return androidLocation.ToLocation(); @@ -110,11 +117,14 @@ void HandleLocation(AndroidLocation location) void Cancel() { RemoveUpdates(); - tcs.TrySetResult(listener.BestLocation); + tcs.TrySetResult(listener?.BestLocation); } void RemoveUpdates() { + if (LocationManager is null) + return; + for (var i = 0; i < providers.Count; i++) LocationManager.RemoveUpdates(listener); } @@ -135,13 +145,17 @@ public async Task StartListeningForegroundAsync(GeolocationListeningReques { ArgumentNullException.ThrowIfNull(request); + if (LocationManager is null) + throw new FeatureNotSupportedException("Android LocationManager is not available"); + if (IsListeningForeground) throw new InvalidOperationException("Already listening to location changes."); await Permissions.EnsureGrantedOrRestrictedAsync(); var enabledProviders = LocationManager.GetProviders(true); - var hasProviders = enabledProviders.Any(p => !ignoredProviders.Contains(p)); + var hasProviders = enabledProviders is not null && + enabledProviders.Any(p => !ignoredProviders.Contains(p)); if (!hasProviders) throw new FeatureNotEnabledException("Location services are not enabled on device."); @@ -198,13 +212,14 @@ void HandleError(GeolocationError geolocationError) /// public void StopListeningForeground() { - if (continuousListener == null) + if (continuousListener is null) return; continuousListener.LocationHandler = null; continuousListener.ErrorHandler = null; - if (listeningProviders == null) + if (listeningProviders is null || + LocationManager is null) return; for (var i = 0; i < listeningProviders.Count; i++) @@ -215,7 +230,7 @@ public void StopListeningForeground() continuousListener = null; } - static (string Provider, float Accuracy) GetBestProvider(LocationManager locationManager, GeolocationAccuracy accuracy) + static (string? Provider, float Accuracy) GetBestProvider(LocationManager locationManager, GeolocationAccuracy accuracy) { // Criteria: https://developer.android.com/reference/android/location/Criteria @@ -268,9 +283,9 @@ public void StopListeningForeground() return (provider, accuracyDistance); } - internal static bool IsBetterLocation(AndroidLocation location, AndroidLocation bestLocation) + internal static bool IsBetterLocation(AndroidLocation location, AndroidLocation? bestLocation) { - if (bestLocation == null) + if (bestLocation is null) return true; var timeDelta = location.Time - bestLocation.Time; @@ -311,15 +326,15 @@ class SingleLocationListener : Java.Lang.Object, ILocationListener float desiredAccuracy; - internal AndroidLocation BestLocation { get; set; } + internal AndroidLocation? BestLocation { get; set; } HashSet activeProviders = new HashSet(); bool wasRaised = false; - internal Action LocationHandler { get; set; } + internal Action? LocationHandler { get; set; } - internal SingleLocationListener(LocationManager manager, float desiredAccuracy, IEnumerable activeProviders) + internal SingleLocationListener(LocationManager? manager, float desiredAccuracy, IEnumerable activeProviders) { this.desiredAccuracy = desiredAccuracy; @@ -327,8 +342,8 @@ internal SingleLocationListener(LocationManager manager, float desiredAccuracy, foreach (var provider in activeProviders) { - var location = manager.GetLastKnownLocation(provider); - if (location != null && GeolocationImplementation.IsBetterLocation(location, BestLocation)) + var location = manager?.GetLastKnownLocation(provider); + if (location is not null && GeolocationImplementation.IsBetterLocation(location, BestLocation)) BestLocation = location; } } @@ -365,8 +380,11 @@ void ILocationListener.OnProviderEnabled(string provider) activeProviders.Add(provider); } - void ILocationListener.OnStatusChanged(string provider, Availability status, Bundle extras) + void ILocationListener.OnStatusChanged(string? provider, Availability status, Bundle? extras) { + if (provider is null) + return; + switch (status) { case Availability.Available: @@ -382,24 +400,21 @@ void ILocationListener.OnStatusChanged(string provider, Availability status, Bun class ContinuousLocationListener : Java.Lang.Object, ILocationListener { - readonly LocationManager manager; - float desiredAccuracy; HashSet activeProviders = new HashSet(); - internal Action LocationHandler { get; set; } + internal Action? LocationHandler { get; set; } - internal Action ErrorHandler { get; set; } + internal Action? ErrorHandler { get; set; } - internal ContinuousLocationListener(LocationManager manager, float desiredAccuracy, IEnumerable providers) + internal ContinuousLocationListener(LocationManager? manager, float desiredAccuracy, IEnumerable providers) { - this.manager = manager; this.desiredAccuracy = desiredAccuracy; foreach (var provider in providers) { - if (manager.IsProviderEnabled(provider)) + if (manager is not null && manager.IsProviderEnabled(provider)) activeProviders.Add(provider); } } @@ -429,8 +444,11 @@ void ILocationListener.OnProviderEnabled(string provider) activeProviders.Add(provider); } - void ILocationListener.OnStatusChanged(string provider, Availability status, Bundle extras) + void ILocationListener.OnStatusChanged(string? provider, Availability status, Bundle? extras) { + if (provider is null) + return; + switch (status) { case Availability.Available: diff --git a/src/Essentials/src/Geolocation/Geolocation.ios.macos.cs b/src/Essentials/src/Geolocation/Geolocation.ios.macos.cs index e2c93bace244..1f3bb82c18ac 100644 --- a/src/Essentials/src/Geolocation/Geolocation.ios.macos.cs +++ b/src/Essentials/src/Geolocation/Geolocation.ios.macos.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Linq; using System.Threading; @@ -10,14 +11,14 @@ namespace Microsoft.Maui.Devices.Sensors { partial class GeolocationImplementation : IGeolocation { - CLLocationManager listeningManager; + CLLocationManager? listeningManager; /// /// Indicates if currently listening to location updates while the app is in foreground. /// public bool IsListeningForeground { get => listeningManager != null; } - public async Task GetLastKnownLocationAsync() + public async Task GetLastKnownLocationAsync() { if (!CLLocationManager.LocationServicesEnabled) throw new FeatureNotEnabledException("Location services are not enabled on device."); @@ -37,7 +38,7 @@ public async Task GetLastKnownLocationAsync() return location?.ToLocation(reducedAccuracy); } - public async Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) + public async Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(request); @@ -50,7 +51,7 @@ public async Task GetLocationAsync(GeolocationRequest request, Cancell // so just use the main loop var manager = MainThread.InvokeOnMainThread(() => new CLLocationManager()); - var tcs = new TaskCompletionSource(manager); + var tcs = new TaskCompletionSource(manager); var listener = new SingleLocationListener(); listener.LocationHandler += HandleLocation; @@ -155,7 +156,7 @@ public async Task StartListeningForegroundAsync(GeolocationListeningReques void HandleLocation(CLLocation clLocation) { - OnLocationChanged(clLocation?.ToLocation(reducedAccuracy)); + OnLocationChanged(clLocation.ToLocation(reducedAccuracy)); } void HandleError(GeolocationError error) @@ -172,7 +173,8 @@ void HandleError(GeolocationError error) /// public void StopListeningForeground() { - if (!IsListeningForeground) + if (!IsListeningForeground || + listeningManager is null) return; listeningManager.StopUpdatingLocation(); @@ -183,7 +185,7 @@ public void StopListeningForeground() listener.ErrorHandler = null; } - listeningManager.Delegate = null; + listeningManager.WeakDelegate = null; listeningManager = null; } @@ -193,7 +195,7 @@ class SingleLocationListener : CLLocationManagerDelegate { bool wasRaised = false; - internal Action LocationHandler { get; set; } + internal Action? LocationHandler { get; set; } /// public override void LocationsUpdated(CLLocationManager manager, CLLocation[] locations) @@ -217,9 +219,9 @@ public override void LocationsUpdated(CLLocationManager manager, CLLocation[] lo class ContinuousLocationListener : CLLocationManagerDelegate { - internal Action LocationHandler { get; set; } + internal Action? LocationHandler { get; set; } - internal Action ErrorHandler { get; set; } + internal Action? ErrorHandler { get; set; } /// public override void LocationsUpdated(CLLocationManager manager, CLLocation[] locations) diff --git a/src/Essentials/src/Geolocation/Geolocation.netstandard.tvos.watchos.cs b/src/Essentials/src/Geolocation/Geolocation.netstandard.tvos.watchos.cs index 4fc89b6e5a1e..5f6d413a3da2 100644 --- a/src/Essentials/src/Geolocation/Geolocation.netstandard.tvos.watchos.cs +++ b/src/Essentials/src/Geolocation/Geolocation.netstandard.tvos.watchos.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Threading; using System.Threading.Tasks; @@ -7,10 +8,10 @@ namespace Microsoft.Maui.Devices.Sensors { partial class GeolocationImplementation : IGeolocation { - public Task GetLastKnownLocationAsync() => + public Task GetLastKnownLocationAsync() => throw ExceptionUtils.NotSupportedOrImplementedException; - public Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) => + public Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) => throw ExceptionUtils.NotSupportedOrImplementedException; public bool IsListeningForeground { get => false; } diff --git a/src/Essentials/src/Geolocation/Geolocation.tizen.cs b/src/Essentials/src/Geolocation/Geolocation.tizen.cs index 9ed480844860..d8491022fd0e 100644 --- a/src/Essentials/src/Geolocation/Geolocation.tizen.cs +++ b/src/Essentials/src/Geolocation/Geolocation.tizen.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Threading; using System.Threading.Tasks; @@ -12,15 +13,15 @@ partial class GeolocationImplementation : IGeolocation public bool IsListeningForeground { get => false; } - public Task GetLastKnownLocationAsync() => Task.FromResult(lastKnownLocation); + public Task GetLastKnownLocationAsync() => Task.FromResult(lastKnownLocation); - public async Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) + public async Task GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(request); await Permissions.EnsureGrantedAsync(); - Locator service = null; + Locator? service = null; var gps = PlatformUtils.GetFeatureInfo("location.gps"); var wps = PlatformUtils.GetFeatureInfo("location.wps"); if (gps) diff --git a/src/Essentials/src/Geolocation/GeolocationAccuracyExtensionMethods.uwp.cs b/src/Essentials/src/Geolocation/GeolocationAccuracyExtensionMethods.uwp.cs index aa5bad7d6e05..ce9461fc1a58 100644 --- a/src/Essentials/src/Geolocation/GeolocationAccuracyExtensionMethods.uwp.cs +++ b/src/Essentials/src/Geolocation/GeolocationAccuracyExtensionMethods.uwp.cs @@ -1,4 +1,6 @@ -namespace Microsoft.Maui.Devices.Sensors +#nullable enable + +namespace Microsoft.Maui.Devices.Sensors { static class GeolocationAccuracyExtensionMethods { diff --git a/src/Essentials/src/Geolocation/GeolocationRequest.shared.cs b/src/Essentials/src/Geolocation/GeolocationRequest.shared.cs index 4715414620d2..7708eed46de5 100644 --- a/src/Essentials/src/Geolocation/GeolocationRequest.shared.cs +++ b/src/Essentials/src/Geolocation/GeolocationRequest.shared.cs @@ -1,3 +1,4 @@ +#nullable enable using System; namespace Microsoft.Maui.Devices.Sensors diff --git a/src/Essentials/src/Geolocation/GeolocationRequest.uwp.cs b/src/Essentials/src/Geolocation/GeolocationRequest.uwp.cs index c35c2605fad3..240d0668444b 100644 --- a/src/Essentials/src/Geolocation/GeolocationRequest.uwp.cs +++ b/src/Essentials/src/Geolocation/GeolocationRequest.uwp.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace Microsoft.Maui.Devices.Sensors { public partial class GeolocationRequest diff --git a/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt index c64aedf4601a..f4e82ef15b8e 100644 --- a/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler? Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler! +override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string! static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler? diff --git a/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt index c64aedf4601a..f4e82ef15b8e 100644 --- a/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler? Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler! +override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string! static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler? diff --git a/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index c64aedf4601a..f4e82ef15b8e 100644 --- a/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler? Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler! +override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string! static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler? diff --git a/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt index c64aedf4601a..f4e82ef15b8e 100644 --- a/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler? Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler! +override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string! static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler? diff --git a/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt index c64aedf4601a..f4e82ef15b8e 100644 --- a/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler? Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler! +override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string! static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler? diff --git a/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt index c64aedf4601a..f4e82ef15b8e 100644 --- a/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler? Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler! +override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string! static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler? diff --git a/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt index c64aedf4601a..f4e82ef15b8e 100644 --- a/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler? Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler! +override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string! static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task! Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler?