File: Accelerometer\AccelerometerQueue.shared.cs
Web Access
Project: src\src\Essentials\src\Essentials.csproj (Microsoft.Maui.Essentials)
namespace Microsoft.Maui.Devices.Sensors
{
	// Detect if 3/4ths of the accelerometer events in the last half second are accelerating
	// this means we are free falling or shaking
	class AccelerometerQueue
	{
		readonly AccelerometerDataPool pool = new AccelerometerDataPool();
 
		// in nanoseconds
		readonly long maxWindowSize = 500_000_000;
		readonly long minWindowSize = 250_000_000;
 
		readonly int minQueueSize = 4;
 
		AccelerometerSample oldest;
		AccelerometerSample newest;
		int sampleCount;
		int acceleratingCount;
 
		internal void Add(long timestamp, bool accelerating)
		{
			Purge(timestamp - maxWindowSize);
			var added = pool.Acquire();
			added.Timestamp = timestamp;
			added.IsAccelerating = accelerating;
			added.Next = null;
 
			if (newest != null)
				newest.Next = added;
 
			newest = added;
 
			if (oldest == null)
				oldest = added;
 
			sampleCount++;
 
			if (accelerating)
				acceleratingCount++;
		}
 
		internal void Clear()
		{
			while (oldest != null)
			{
				var removed = oldest;
				oldest = removed.Next;
				pool.Release(removed);
			}
			newest = null;
			sampleCount = 0;
			acceleratingCount = 0;
		}
 
		void Purge(long cutoff)
		{
			while (sampleCount >= minQueueSize &&
				   oldest != null &&
				   cutoff - oldest.Timestamp > 0)
			{
				var removed = oldest;
				if (removed.IsAccelerating)
					acceleratingCount--;
 
				sampleCount--;
				oldest = removed.Next;
 
				if (oldest == null)
					newest = null;
 
				pool.Release(removed);
			}
		}
 
		// Returns true if we have enough samples to detect if we are shaking the device and that more than 3/4th of them are accelerating
		internal bool IsShaking => newest != null &&
						  oldest != null &&
						  newest.Timestamp - oldest.Timestamp >= minWindowSize &&
						  acceleratingCount >= (sampleCount >> 1) + (sampleCount >> 2);
 
		internal class AccelerometerSample
		{
			public long Timestamp { get; set; }
 
			public bool IsAccelerating { get; set; }
 
			public AccelerometerSample Next { get; set; }
		}
 
		internal class AccelerometerDataPool
		{
			AccelerometerSample head;
 
			internal AccelerometerSample Acquire()
			{
				var aquired = head;
				if (aquired == null)
					aquired = new AccelerometerSample();
				else
					head = aquired.Next;
 
				return aquired;
			}
 
			internal void Release(AccelerometerSample sample)
			{
				sample.Next = head;
				head = sample;
			}
		}
	}
}