Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geolocation: Add #nullable for Essentials Geolocation code #13371

Merged
merged 7 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 44 additions & 26 deletions src/Essentials/src/Geolocation/Geolocation.android.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -18,24 +19,27 @@ partial class GeolocationImplementation : IGeolocation
const long twoMinutes = 120000;
static readonly string[] ignoredProviders = new string[] { LocationManager.PassiveProvider, "local_database" };

static ContinuousLocationListener continuousListener;
static List<string> listeningProviders;
static ContinuousLocationListener? continuousListener;
static List<string>? listeningProviders;

static LocationManager locationManager;
static LocationManager? locationManager;

static LocationManager LocationManager =>
static LocationManager? LocationManager =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if LocationManager can ever be null on a sane Android environment, but there might be some weird custom ROMs; I guess the best is to throw FeatureNotSupportedException then...

Copy link
Contributor Author

@vividos vividos Feb 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfversluis there are still my three questions...

locationManager ??= Application.Context.GetSystemService(Context.LocationService) as LocationManager;

/// <summary>
/// Indicates if currently listening to location updates while the app is in foreground.
/// </summary>
public bool IsListeningForeground { get => continuousListener != null; }

public async Task<Location> GetLastKnownLocationAsync()
public async Task<Location?> GetLastKnownLocationAsync()
{
if (LocationManager is null)
throw new FeatureNotSupportedException("Android LocationManager is not available");

await Permissions.EnsureGrantedOrRestrictedAsync<Permissions.LocationWhenInUse>();

AndroidLocation bestLocation = null;
AndroidLocation? bestLocation = null;

foreach (var provider in LocationManager.GetProviders(true))
{
Expand All @@ -48,10 +52,13 @@ public async Task<Location> GetLastKnownLocationAsync()
return bestLocation?.ToLocation();
}

public async Task<Location> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken)
public async Task<Location?> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(request);

if (LocationManager is null)
throw new FeatureNotSupportedException("Android LocationManager is not available");

await Permissions.EnsureGrantedOrRestrictedAsync<Permissions.LocationWhenInUse>();

var enabledProviders = LocationManager.GetProviders(true);
Expand All @@ -68,7 +75,7 @@ public async Task<Location> GetLocationAsync(GeolocationRequest request, Cancell
if (string.IsNullOrEmpty(providerInfo.Provider))
return await GetLastKnownLocationAsync();

var tcs = new TaskCompletionSource<AndroidLocation>();
var tcs = new TaskCompletionSource<AndroidLocation?>();

var allProviders = LocationManager.GetProviders(false);

Expand Down Expand Up @@ -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);
}
Expand All @@ -135,13 +145,17 @@ public async Task<bool> 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<Permissions.LocationWhenInUse>();

var enabledProviders = LocationManager.GetProviders(true);
var hasProviders = enabledProviders.Any(p => !ignoredProviders.Contains(p));
var hasProviders = enabledProviders != null &&
enabledProviders.Any(p => !ignoredProviders.Contains(p));
vividos marked this conversation as resolved.
Show resolved Hide resolved

if (!hasProviders)
throw new FeatureNotEnabledException("Location services are not enabled on device.");
Expand Down Expand Up @@ -204,7 +218,8 @@ public void StopListeningForeground()
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++)
Expand All @@ -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

Expand Down Expand Up @@ -268,7 +283,7 @@ 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)
return true;
Expand Down Expand Up @@ -311,23 +326,23 @@ class SingleLocationListener : Java.Lang.Object, ILocationListener

float desiredAccuracy;

internal AndroidLocation BestLocation { get; set; }
internal AndroidLocation? BestLocation { get; set; }

HashSet<string> activeProviders = new HashSet<string>();

bool wasRaised = false;

internal Action<AndroidLocation> LocationHandler { get; set; }
internal Action<AndroidLocation>? LocationHandler { get; set; }

internal SingleLocationListener(LocationManager manager, float desiredAccuracy, IEnumerable<string> activeProviders)
internal SingleLocationListener(LocationManager? manager, float desiredAccuracy, IEnumerable<string> activeProviders)
{
this.desiredAccuracy = desiredAccuracy;

this.activeProviders = new HashSet<string>(activeProviders);

foreach (var provider in activeProviders)
{
var location = manager.GetLastKnownLocation(provider);
var location = manager?.GetLastKnownLocation(provider);
if (location != null && GeolocationImplementation.IsBetterLocation(location, BestLocation))
BestLocation = location;
}
Expand Down Expand Up @@ -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:
Expand All @@ -382,24 +400,21 @@ void ILocationListener.OnStatusChanged(string provider, Availability status, Bun

class ContinuousLocationListener : Java.Lang.Object, ILocationListener
{
readonly LocationManager manager;

float desiredAccuracy;

HashSet<string> activeProviders = new HashSet<string>();

internal Action<AndroidLocation> LocationHandler { get; set; }
internal Action<AndroidLocation>? LocationHandler { get; set; }

internal Action<GeolocationError> ErrorHandler { get; set; }
internal Action<GeolocationError>? ErrorHandler { get; set; }

internal ContinuousLocationListener(LocationManager manager, float desiredAccuracy, IEnumerable<string> providers)
internal ContinuousLocationListener(LocationManager? manager, float desiredAccuracy, IEnumerable<string> providers)
{
this.manager = manager;
this.desiredAccuracy = desiredAccuracy;

foreach (var provider in providers)
{
if (manager.IsProviderEnabled(provider))
if (manager != null && manager.IsProviderEnabled(provider))
vividos marked this conversation as resolved.
Show resolved Hide resolved
activeProviders.Add(provider);
}
}
Expand Down Expand Up @@ -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 == null)
vividos marked this conversation as resolved.
Show resolved Hide resolved
return;

switch (status)
{
case Availability.Available:
Expand Down
22 changes: 12 additions & 10 deletions src/Essentials/src/Geolocation/Geolocation.ios.macos.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
using System;
using System.Linq;
using System.Threading;
Expand All @@ -10,14 +11,14 @@ namespace Microsoft.Maui.Devices.Sensors
{
partial class GeolocationImplementation : IGeolocation
{
CLLocationManager listeningManager;
CLLocationManager? listeningManager;

/// <summary>
/// Indicates if currently listening to location updates while the app is in foreground.
/// </summary>
public bool IsListeningForeground { get => listeningManager != null; }

public async Task<Location> GetLastKnownLocationAsync()
public async Task<Location?> GetLastKnownLocationAsync()
{
if (!CLLocationManager.LocationServicesEnabled)
throw new FeatureNotEnabledException("Location services are not enabled on device.");
Expand All @@ -37,7 +38,7 @@ public async Task<Location> GetLastKnownLocationAsync()
return location?.ToLocation(reducedAccuracy);
}

public async Task<Location> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken)
public async Task<Location?> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(request);

Expand All @@ -50,7 +51,7 @@ public async Task<Location> GetLocationAsync(GeolocationRequest request, Cancell
// so just use the main loop
var manager = MainThread.InvokeOnMainThread(() => new CLLocationManager());

var tcs = new TaskCompletionSource<CLLocation>(manager);
var tcs = new TaskCompletionSource<CLLocation?>(manager);

var listener = new SingleLocationListener();
listener.LocationHandler += HandleLocation;
Expand Down Expand Up @@ -155,7 +156,7 @@ public async Task<bool> StartListeningForegroundAsync(GeolocationListeningReques

void HandleLocation(CLLocation clLocation)
{
OnLocationChanged(clLocation?.ToLocation(reducedAccuracy));
OnLocationChanged(clLocation.ToLocation(reducedAccuracy));
}

void HandleError(GeolocationError error)
Expand All @@ -172,7 +173,8 @@ void HandleError(GeolocationError error)
/// </summary>
public void StopListeningForeground()
{
if (!IsListeningForeground)
if (!IsListeningForeground ||
listeningManager is null)
return;

listeningManager.StopUpdatingLocation();
Expand All @@ -183,7 +185,7 @@ public void StopListeningForeground()
listener.ErrorHandler = null;
}

listeningManager.Delegate = null;
listeningManager.WeakDelegate = null;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure setting the WeakDelegate is the proper thing to do here, but since the Delegate now is non-nullable, it can't be set to null anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfversluis the third...


listeningManager = null;
}
Expand All @@ -193,7 +195,7 @@ class SingleLocationListener : CLLocationManagerDelegate
{
bool wasRaised = false;

internal Action<CLLocation> LocationHandler { get; set; }
internal Action<CLLocation>? LocationHandler { get; set; }

/// <inheritdoc/>
public override void LocationsUpdated(CLLocationManager manager, CLLocation[] locations)
Expand All @@ -217,9 +219,9 @@ public override void LocationsUpdated(CLLocationManager manager, CLLocation[] lo

class ContinuousLocationListener : CLLocationManagerDelegate
{
internal Action<CLLocation> LocationHandler { get; set; }
internal Action<CLLocation>? LocationHandler { get; set; }

internal Action<GeolocationError> ErrorHandler { get; set; }
internal Action<GeolocationError>? ErrorHandler { get; set; }

/// <inheritdoc/>
public override void LocationsUpdated(CLLocationManager manager, CLLocation[] locations)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
using System;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -7,10 +8,10 @@ namespace Microsoft.Maui.Devices.Sensors
{
partial class GeolocationImplementation : IGeolocation
{
public Task<Location> GetLastKnownLocationAsync() =>
public Task<Location?> GetLastKnownLocationAsync() =>
throw ExceptionUtils.NotSupportedOrImplementedException;

public Task<Location> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) =>
public Task<Location?> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken) =>
throw ExceptionUtils.NotSupportedOrImplementedException;

public bool IsListeningForeground { get => false; }
Expand Down
7 changes: 4 additions & 3 deletions src/Essentials/src/Geolocation/Geolocation.tizen.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
using System;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -12,15 +13,15 @@ partial class GeolocationImplementation : IGeolocation

public bool IsListeningForeground { get => false; }

public Task<Location> GetLastKnownLocationAsync() => Task.FromResult(lastKnownLocation);
public Task<Location?> GetLastKnownLocationAsync() => Task.FromResult<Location?>(lastKnownLocation);

public async Task<Location> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken)
public async Task<Location?> GetLocationAsync(GeolocationRequest request, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(request);

await Permissions.EnsureGrantedAsync<Permissions.LocationWhenInUse>();

Locator service = null;
Locator? service = null;
var gps = PlatformUtils.GetFeatureInfo<bool>("location.gps");
var wps = PlatformUtils.GetFeatureInfo<bool>("location.wps");
if (gps)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Microsoft.Maui.Devices.Sensors
#nullable enable

namespace Microsoft.Maui.Devices.Sensors
{
static class GeolocationAccuracyExtensionMethods
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#nullable enable
using System;

namespace Microsoft.Maui.Devices.Sensors
Expand Down
2 changes: 2 additions & 0 deletions src/Essentials/src/Geolocation/GeolocationRequest.uwp.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable

namespace Microsoft.Maui.Devices.Sensors
{
public partial class GeolocationRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi
Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>?
Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>!
override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string!
static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool
Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi
Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>?
Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>!
override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string!
static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool
Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi
Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>?
Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>!
override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string!
static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool
Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi
Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>?
Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>!
override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string!
static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool
Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationLocationChangedEventArgs!>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs.GeolocationLi
Microsoft.Maui.Devices.Sensors.IGeolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>?
Microsoft.Maui.Storage.ISecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
static Microsoft.Maui.Devices.Sensors.Geolocation.ListeningFailed -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationListeningFailedEventArgs!>!
override Microsoft.Maui.Devices.Sensors.GeolocationRequest.ToString() -> string!
static Microsoft.Maui.Storage.SecureStorage.GetAsync(string! key) -> System.Threading.Tasks.Task<string?>!
Microsoft.Maui.Devices.Sensors.IGeolocation.IsListeningForeground.get -> bool
Microsoft.Maui.Devices.Sensors.IGeolocation.LocationChanged -> System.EventHandler<Microsoft.Maui.Devices.Sensors.GeolocationLocationChangedEventArgs!>?
Expand Down
Loading