File: Geolocation\Geolocation.shared.cs
Web Access
Project: src\src\Essentials\src\Essentials.csproj (Microsoft.Maui.Essentials)
#nullable enable
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Maui.ApplicationModel;
 
namespace Microsoft.Maui.Devices.Sensors
{
	/// <summary>
	/// Provides a way to get the current location of the device.
	/// </summary>
	public interface IGeolocation
	{
		/// <summary>
		/// Returns the last known location of the device.
		/// </summary>
		/// <returns>A <see cref="Location"/> object containing recent location information or <see langword="null"/> if no location is known.</returns>
		/// <remarks>
		/// <para>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</para>
		/// <para>This location may be a recently cached location.</para>
		/// </remarks>
		Task<Location?> GetLastKnownLocationAsync();
 
		/// <summary>
		/// Returns the current location of the device.
		/// </summary>
		/// <param name="request">The criteria to use when determining the location of the device.</param>
		/// <param name="cancelToken">A token that can be used for cancelling the operation.</param>
		/// <remarks>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</remarks>
		/// <returns>A <see cref="Location"/> object containing current location information or <see langword="null"/> if no location could be determined.</returns>
		Task<Location?> GetLocationAsync(GeolocationRequest request, CancellationToken cancelToken);
 
		/// <summary>
		/// Indicates if currently listening to location updates while the app is in foreground.
		/// </summary>
		bool IsListeningForeground { get; }
 
		/// <summary>
		/// Occurs while listening to location updates.
		/// </summary>
		event EventHandler<GeolocationLocationChangedEventArgs>? LocationChanged;
 
		/// <summary>
		/// Occurs when an error during listening for location updates arises. When the event is
		/// fired, listening for further location updates has been stopped and no further
		/// <see cref="LocationChanged"/> events are sent.
		/// </summary>
		event EventHandler<GeolocationListeningFailedEventArgs>? ListeningFailed;
 
		/// <summary>
		/// Starts listening to location updates using the <see cref="LocationChanged"/> event. Events
		/// may only sent when the app is in the foreground. Requests
		/// <see cref="Permissions.LocationWhenInUse"/> from the user.
		/// </summary>
		/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>.</exception>
		/// <exception cref="FeatureNotSupportedException">Thrown if listening is not supported on this platform.</exception>
		/// <exception cref="InvalidOperationException">Thrown if already listening and <see cref="IsListeningForeground"/> returns <see langword="true"/>.</exception>
		/// <param name="request">The listening request parameters to use.</param>
		/// <returns><see langword="true"/> when listening was started, or <see langword="false"/> when listening couldn't be started.</returns>
		Task<bool> StartListeningForegroundAsync(GeolocationListeningRequest request);
 
		/// <summary>
		/// Stop listening for location updates when the app is in the foreground.
		/// Has no effect when <see cref="IsListeningForeground"/> is currently <see langword="false"/>.
		/// </summary>
		void StopListeningForeground();
	}
 
	/// <summary>
	/// Provides a way to get the current location of the device.
	/// </summary>
	public static partial class Geolocation
	{
		/// <summary>
		/// Returns the last known location of the device.
		/// </summary>
		/// <returns>A <see cref="Location"/> object containing recent location information or <see langword="null"/> if no location is known.</returns>
		/// <remarks>
		/// <para>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</para>
		/// <para>This location may be a recently cached location.</para>
		/// </remarks>
		public static Task<Location?> GetLastKnownLocationAsync() =>
			Current.GetLastKnownLocationAsync();
 
		/// <summary>
		/// Returns the current location of the device.
		/// </summary>
		/// <returns>A <see cref="Location"/> object containing current location information or <see langword="null"/> if no location could be determined.</returns>
		/// <remarks>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</remarks>
		public static Task<Location?> GetLocationAsync() =>
			Current.GetLocationAsync();
 
		/// <summary>
		/// Returns the current location of the device.
		/// </summary>
		/// <param name="request">The criteria to use when determining the location of the device.</param>
		/// <returns>A <see cref="Location"/> object containing current location information or <see langword="null"/> if no location could be determined.</returns>
		/// <remarks>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</remarks>
		public static Task<Location?> GetLocationAsync(GeolocationRequest request) =>
			Current.GetLocationAsync(request);
 
		/// <summary>
		/// Returns the current location of the device.
		/// </summary>
		/// <param name="request">The criteria to use when determining the location of the device.</param>
		/// <param name="cancelToken">A token that can be used for cancelling the operation.</param>
		/// <returns>A <see cref="Location"/> object containing current location information or <see langword="null"/> if no location could be determined.</returns>
		/// <remarks>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</remarks>
		public static Task<Location?> GetLocationAsync(GeolocationRequest request, CancellationToken cancelToken) =>
			Current.GetLocationAsync(request, cancelToken);
 
		/// <summary>
		/// Indicates if currently listening to location updates while the app is in foreground.
		/// </summary>
		public static bool IsListeningForeground { get => Current.IsListeningForeground; }
 
		/// <summary>
		/// Occurs while listening to location updates.
		/// </summary>
		public static event EventHandler<GeolocationLocationChangedEventArgs> LocationChanged
		{
			add => Current.LocationChanged += value;
			remove => Current.LocationChanged -= value;
		}
 
		/// <summary>
		/// Occurs when an error during listening for location updates arises. When the event is
		/// fired, listening for further location updates has been stopped and no further
		/// <see cref="LocationChanged"/> events are sent.
		/// </summary>
		public static event EventHandler<GeolocationListeningFailedEventArgs> ListeningFailed
		{
			add => Current.ListeningFailed += value;
			remove => Current.ListeningFailed -= value;
		}
 
		/// <summary>
		/// Starts listening to location updates using the <see cref="Geolocation.LocationChanged"/>
		/// event or the <see cref="Geolocation.ListeningFailed"/> event. Events may only sent when
		/// the app is in the foreground. Requests <see cref="Permissions.LocationWhenInUse"/>
		/// from the user.
		/// </summary>
		/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>.</exception>
		/// <exception cref="FeatureNotSupportedException">Thrown if listening is not supported on this platform.</exception>
		/// <exception cref="InvalidOperationException">Thrown if already listening and <see cref="IsListeningForeground"/> returns <see langword="true"/>.</exception>
		/// <param name="request">The listening request parameters to use.</param>
		/// <returns><see langword="true"/> when listening was started, or <see langword="false"/> when listening couldn't be started.</returns>
		public static Task<bool> StartListeningForegroundAsync(GeolocationListeningRequest request) =>
			Current.StartListeningForegroundAsync(request);
 
		/// <summary>
		/// Stop listening for location updates when the app is in the foreground.
		/// Has no effect when not listening and <see cref="Geolocation.IsListeningForeground"/>
		/// is currently <see langword="false"/>.
		/// </summary>
		public static void StopListeningForeground() =>
			Current.StopListeningForeground();
 
		static IGeolocation Current => Devices.Sensors.Geolocation.Default;
 
		static IGeolocation? defaultImplementation;
 
		/// <summary>
		/// Provides the default implementation for static usage of this API.
		/// </summary>
		public static IGeolocation Default =>
			defaultImplementation ??= new GeolocationImplementation();
 
		internal static void SetDefault(IGeolocation? implementation) =>
			defaultImplementation = implementation;
	}
 
	partial class GeolocationImplementation : IGeolocation
	{
		public event EventHandler<GeolocationLocationChangedEventArgs>? LocationChanged;
 
		public event EventHandler<GeolocationListeningFailedEventArgs>? ListeningFailed;
 
		internal void OnLocationChanged(Location location) =>
			OnLocationChanged(new GeolocationLocationChangedEventArgs(location));
 
		internal void OnLocationChanged(GeolocationLocationChangedEventArgs e) =>
			LocationChanged?.Invoke(null, e);
 
		internal void OnLocationError(GeolocationError geolocationError) =>
			ListeningFailed?.Invoke(null, new GeolocationListeningFailedEventArgs(geolocationError));
	}
 
	/// <summary>
	/// Static class with extension methods for the <see cref="IGeolocation"/> APIs.
	/// </summary>
	public static class GeolocationExtensions
	{
		/// <summary>
		/// Returns the current location of the device.
		/// </summary>
		/// <param name="geolocation">The object this method is invoked on.</param>
		/// <returns>A <see cref="Location"/> object containing current location information or <see langword="null"/> if no location could be determined.</returns>
		/// <remarks>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</remarks>
		public static Task<Location?> GetLocationAsync(this IGeolocation geolocation) =>
			geolocation.GetLocationAsync(new GeolocationRequest(), default);
 
		/// <summary>
		/// Returns the current location of the device.
		/// </summary>
		/// <param name="geolocation">The object this method is invoked on.</param>
		/// <param name="request">The criteria to use when determining the location of the device.</param>
		/// <returns>A <see cref="Location"/> object containing current location information or <see langword="null"/> if no location could be determined.</returns>
		/// <remarks>The location permissions will be requested at runtime if needed. You might still need to declare something in your app manifest.</remarks>
		public static Task<Location?> GetLocationAsync(this IGeolocation geolocation, GeolocationRequest request) =>
			geolocation.GetLocationAsync(request ?? new GeolocationRequest(), default);
	}
}