File: Battery\Battery.shared.cs
Web Access
Project: src\src\Essentials\src\Essentials.csproj (Microsoft.Maui.Essentials)
#nullable enable
using System;
 
namespace Microsoft.Maui.Devices
{
	/// <summary>
	/// Methods and properties for battery and charging information of the device.
	/// </summary>
	/// <remarks>
	/// <para>Platform specific remarks:</para>
	/// <para>- Android: <c>Battery_Stats</c> permission must be set in manifest.</para>
	/// <para>- iOS: Simulator will not return battery information, must be run on device.</para>
	/// <para>- Windows: None.</para>
	/// </remarks>
	public interface IBattery
	{
		/// <summary>
		/// Gets the current charge level of the device from 0.0 to 1.0.
		/// </summary>
		/// <remarks>Returns -1 if no battery exists.</remarks>
		double ChargeLevel { get; }
 
		/// <summary>
		/// Gets the charging state of the device.
		/// </summary>
		BatteryState State { get; }
 
		/// <summary>
		/// Gets the current power source for the device.
		/// </summary>
		BatteryPowerSource PowerSource { get; }
 
		/// <summary>
		/// Gets the current energy saver status of the device.
		/// </summary>
		EnergySaverStatus EnergySaverStatus { get; }
 
		/// <summary>
		/// Occurs when battery properties change.
		/// </summary>
		event EventHandler<BatteryInfoChangedEventArgs> BatteryInfoChanged;
 
		/// <summary>
		/// Occurs when the energy saver status changes.
		/// </summary>
		event EventHandler<EnergySaverStatusChangedEventArgs> EnergySaverStatusChanged;
	}
 
	/// <summary>
	/// Methods and properties for battery and charging information of the device.
	/// </summary>
	public static class Battery
	{
		/// <summary>
		/// Gets the current charge level of the device from 0.0 to 1.0.
		/// </summary>
		/// <remarks>Returns -1 if no battery exists.</remarks>
		public static double ChargeLevel => Default.ChargeLevel;
 
		/// <summary>
		/// Gets the charging state of the device.
		/// </summary>
		public static BatteryState State => Default.State;
 
		/// <summary>
		/// Gets the current power source for the device.
		/// </summary>
		public static BatteryPowerSource PowerSource => Default.PowerSource;
 
		/// <summary>
		/// Gets the current energy saver status of the device.
		/// </summary>
		public static EnergySaverStatus EnergySaverStatus => Default.EnergySaverStatus;
 
		/// <summary>
		/// Event trigger when battery properties have changed.
		/// </summary>
		public static event EventHandler<BatteryInfoChangedEventArgs> BatteryInfoChanged
		{
			add => Default.BatteryInfoChanged += value;
			remove => Default.BatteryInfoChanged -= value;
		}
 
		/// <summary>
		/// Occurs when the energy saver status changes.
		/// </summary>
		public static event EventHandler<EnergySaverStatusChangedEventArgs> EnergySaverStatusChanged
		{
			add => Default.EnergySaverStatusChanged += value;
			remove => Default.EnergySaverStatusChanged -= value;
		}
 
		static IBattery? defaultImplementation;
 
		/// <summary>
		/// Provides the default implementation for static usage of this API.
		/// </summary>s
		public static IBattery Default =>
			defaultImplementation ??= new BatteryImplementation();
 
		internal static void SetDefault(IBattery? implementation) =>
			defaultImplementation = implementation;
	}
 
	partial class BatteryImplementation : IBattery
	{
		event EventHandler<BatteryInfoChangedEventArgs>? BatteryInfoChangedInternal;
 
		event EventHandler<EnergySaverStatusChangedEventArgs>? EnergySaverStatusChangedInternal;
 
		public event EventHandler<BatteryInfoChangedEventArgs> BatteryInfoChanged
		{
			add
			{
				if (BatteryInfoChangedInternal == null)
					StartBatteryListeners();
				BatteryInfoChangedInternal += value;
			}
			remove
			{
				BatteryInfoChangedInternal -= value;
				if (BatteryInfoChangedInternal == null)
					StopBatteryListeners();
			}
		}
 
		public event EventHandler<EnergySaverStatusChangedEventArgs> EnergySaverStatusChanged
		{
			add
			{
				if (EnergySaverStatusChangedInternal == null)
					StartEnergySaverListeners();
				EnergySaverStatusChangedInternal += value;
			}
			remove
			{
				EnergySaverStatusChangedInternal -= value;
				if (EnergySaverStatusChangedInternal == null)
					StopEnergySaverListeners();
			}
		}
 
		// a cache so that events aren't fired unnecessarily
		// this is mainly an issue on Android, but we can stiil do this everywhere
		static double currentLevel;
		static BatteryPowerSource currentSource;
		static BatteryState currentState;
 
		void SetCurrent()
		{
			currentLevel = ChargeLevel;
			currentSource = PowerSource;
			currentState = State;
		}
 
		void OnBatteryInfoChanged(double level, BatteryState state, BatteryPowerSource source)
			=> OnBatteryInfoChanged(new BatteryInfoChangedEventArgs(level, state, source));
 
		void OnBatteryInfoChanged()
			=> OnBatteryInfoChanged(ChargeLevel, State, PowerSource);
 
		void OnBatteryInfoChanged(BatteryInfoChangedEventArgs e)
		{
			if (currentLevel != e.ChargeLevel || currentSource != e.PowerSource || currentState != e.State)
			{
				SetCurrent();
				BatteryInfoChangedInternal?.Invoke(null, e);
			}
		}
 
		void OnEnergySaverChanged()
			=> OnEnergySaverChanged(EnergySaverStatus);
 
		void OnEnergySaverChanged(EnergySaverStatus saverStatus)
			=> OnEnergySaverChanged(new EnergySaverStatusChangedEventArgs(saverStatus));
 
		void OnEnergySaverChanged(EnergySaverStatusChangedEventArgs e)
			=> EnergySaverStatusChangedInternal?.Invoke(null, e);
	}
 
	/// <summary>
	/// Describes possible states of the battery.
	/// </summary>
	public enum BatteryState
	{
		/// <summary>Battery state could not be determined.</summary>
		Unknown = 0,
 
		/// <summary>Battery is actively being charged by a power source.</summary>
		Charging = 1,
 
		/// <summary>Battery is not plugged in and discharging.</summary>
		Discharging = 2,
 
		/// <summary>Battery is full.</summary>
		Full = 3,
 
		/// <summary>Battery is not charging or discharging, but in an inbetween state.</summary>
		NotCharging = 4,
 
		/// <summary>Battery does not exist on the device.</summary>
		NotPresent = 5
	}
 
	/// <summary>
	/// Enumerates power sources with which the device and battery can be powered or charged.
	/// </summary>
	public enum BatteryPowerSource
	{
		/// <summary>Power source cannot be determined.</summary>
		Unknown = 0,
 
		/// <summary>Power source is the battery and is currently not being charged.</summary>
		Battery = 1,
 
		/// <summary>Power source is an AC Charger.</summary>
		AC = 2,
 
		/// <summary>Power source is a USB port.</summary>
		Usb = 3,
 
		/// <summary>Power source is wireless.</summary>
		Wireless = 4
	}
 
	/// <summary>
	/// Enumerates states that the energy saver van have on the device.
	/// </summary>
	public enum EnergySaverStatus
	{
		/// <summary>Status of energy saving is unknown.</summary>
		Unknown = 0,
 
		/// <summary>Energy saving is on.</summary>
		On = 1,
 
		/// <summary>Energy saving is off.</summary>
		Off = 2
	}
 
	/// <summary>
	/// Event arguments containing the current reading of <see cref="IBattery"/>.
	/// </summary>
	public class BatteryInfoChangedEventArgs : EventArgs
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="BatteryInfoChangedEventArgs"/> class.
		/// </summary>
		/// <param name="level">The current level of the battery.</param>
		/// <param name="state">The state of the battery.</param>
		/// <param name="source">The source of the battery.</param>
		public BatteryInfoChangedEventArgs(double level, BatteryState state, BatteryPowerSource source)
		{
			ChargeLevel = level;
			State = state;
			PowerSource = source;
		}
 
		/// <summary>
		/// Gets the current charge level of the device from 0.0 to 1.0.
		/// </summary>
		public double ChargeLevel { get; }
 
		/// <summary>
		/// Gets the charging state of the device.
		/// </summary>
		public BatteryState State { get; }
 
		/// <summary>
		/// Gets the current power source for the device.
		/// </summary>
		public BatteryPowerSource PowerSource { get; }
 
		/// <summary>
		/// Returns a string representation of this instance of <see cref="BatteryInfoChangedEventArgs"/>.
		/// </summary>
		/// <returns>A string representation of this instance in the format of <c>ChargeLevel: {value}, State: {value}, PowerSource: {value}</c>.</returns>
		public override string ToString() =>
			$"{nameof(ChargeLevel)}: {ChargeLevel.ToString()}, " +
			$"{nameof(State)}: {State}, " +
			$"{nameof(PowerSource)}: {PowerSource}";
	}
 
	/// <summary>
	/// Event arguments when the energy saver status changes.
	/// </summary>
	public class EnergySaverStatusChangedEventArgs : EventArgs
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="EnergySaverStatusChangedEventArgs"/> class.
		/// </summary>
		/// <param name="saverStatus">The current status of the event.</param>
		public EnergySaverStatusChangedEventArgs(EnergySaverStatus saverStatus)
		{
			EnergySaverStatus = saverStatus;
		}
 
		/// <summary>
		/// Gets the current status of energy saver mode.
		/// </summary>
		public EnergySaverStatus EnergySaverStatus { get; }
 
		/// <summary>
		/// Returns a string representation of this instance of <see cref="EnergySaverStatusChangedEventArgs"/>.
		/// </summary>
		/// <returns>A string representation of this instance in the format of <c>EnergySaverStatus: {value}</c>.</returns>
		public override string ToString() =>
			$"{nameof(EnergySaverStatus)}: {EnergySaverStatus}";
	}
}