From 49f5dfd45449e789ca226cd875a7278950111c23 Mon Sep 17 00:00:00 2001 From: Ortes Date: Thu, 4 Jan 2024 11:43:35 +0100 Subject: [PATCH] feat(sensor_plus): Add support for plateform timestamp BREAKING CHANGE: Now all events have a `DateTime` property `timestamp` as constructor 4th constructor parameter --- .../plus/sensors/StreamHandlerImpl.kt | 9 +++++- .../ios/Classes/FPPStreamHandlerPlus.swift | 29 +++++++++++++++---- .../sensors_plus/lib/src/web_sensors.dart | 15 +++++++--- .../lib/src/accelerometer_event.dart | 10 ++++++- .../lib/src/gyroscope_event.dart | 10 ++++++- .../lib/src/magnetometer_event.dart | 10 ++++++- .../lib/src/method_channel_sensors.dart | 28 +++++++++++++++--- .../lib/src/user_accelerometer_event.dart | 16 ++++++++-- 8 files changed, 107 insertions(+), 20 deletions(-) diff --git a/packages/sensors_plus/sensors_plus/android/src/main/kotlin/dev/fluttercommunity/plus/sensors/StreamHandlerImpl.kt b/packages/sensors_plus/sensors_plus/android/src/main/kotlin/dev/fluttercommunity/plus/sensors/StreamHandlerImpl.kt index 54669e2b39a..84b5e543041 100644 --- a/packages/sensors_plus/sensors_plus/android/src/main/kotlin/dev/fluttercommunity/plus/sensors/StreamHandlerImpl.kt +++ b/packages/sensors_plus/sensors_plus/android/src/main/kotlin/dev/fluttercommunity/plus/sensors/StreamHandlerImpl.kt @@ -4,6 +4,7 @@ import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager +import android.os.SystemClock import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel.EventSink @@ -15,6 +16,8 @@ internal class StreamHandlerImpl( private var sensor: Sensor? = null + private var timestampMicroAtBoot: Long = System.currentTimeMillis() * 1000 - SystemClock.elapsedRealtimeNanos() / 1000 + var samplingPeriod = 200000 set(value) { field = value @@ -64,10 +67,14 @@ internal class StreamHandlerImpl( override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} override fun onSensorChanged(event: SensorEvent) { - val sensorValues = DoubleArray(event.values.size) + val sensorValues = DoubleArray(event.values.size + 1) event.values.forEachIndexed { index, value -> sensorValues[index] = value.toDouble() } + + val timestampMicro = timestampMicroAtBoot + (event.timestamp / 1000) + sensorValues[event.values.size] = timestampMicro.toDouble() + events.success(sensorValues) } } diff --git a/packages/sensors_plus/sensors_plus/ios/Classes/FPPStreamHandlerPlus.swift b/packages/sensors_plus/sensors_plus/ios/Classes/FPPStreamHandlerPlus.swift index fa64accc40f..0537cd4faf2 100644 --- a/packages/sensors_plus/sensors_plus/ios/Classes/FPPStreamHandlerPlus.swift +++ b/packages/sensors_plus/sensors_plus/ios/Classes/FPPStreamHandlerPlus.swift @@ -14,6 +14,8 @@ public protocol MotionStreamHandler: FlutterStreamHandler { var samplingPeriod: Int { get set } } +let timestampMicroAtBoot = (Date().timeIntervalSince1970 - ProcessInfo.processInfo.systemUptime) * 1000000 + func _initMotionManager() { if (_motionManager == nil) { _motionManager = CMMotionManager() @@ -24,14 +26,15 @@ func _initMotionManager() { } } -func sendTriplet(x: Float64, y: Float64, z: Float64, sink: @escaping FlutterEventSink) { +func sendFlutter(x: Float64, y: Float64, z: Float64, timestamp: TimeInterval, sink: @escaping FlutterEventSink) { if _isCleanUp { return } // Even after [detachFromEngineForRegistrar] some events may still be received // and fired until fully detached. DispatchQueue.main.async { - let triplet = [x, y, z] + let timestampSince1970Micro = timestampMicroAtBoot + (timestamp * 1000000) + let triplet = [x, y, z, timestampSince1970Micro] triplet.withUnsafeBufferPointer { buffer in sink(FlutterStandardTypedData.init(float64: Data(buffer: buffer))) } @@ -67,10 +70,11 @@ class FPPAccelerometerStreamHandlerPlus: NSObject, MotionStreamHandler { // Multiply by gravity, and adjust sign values to // align with Android. let acceleration = data!.acceleration - sendTriplet( + sendFlutter( x: -acceleration.x * GRAVITY, y: -acceleration.y * GRAVITY, z: -acceleration.z * GRAVITY, + timestamp: data!.timestamp, sink: sink ) } @@ -116,10 +120,11 @@ class FPPUserAccelStreamHandlerPlus: NSObject, MotionStreamHandler { // Multiply by gravity, and adjust sign values to // align with Android. let acceleration = data!.userAcceleration - sendTriplet( + sendFlutter( x: -acceleration.x * GRAVITY, y: -acceleration.y * GRAVITY, z: -acceleration.z * GRAVITY, + timestamp: data!.timestamp, sink: sink ) } @@ -163,7 +168,13 @@ class FPPGyroscopeStreamHandlerPlus: NSObject, MotionStreamHandler { return } let rotationRate = data!.rotationRate - sendTriplet(x: rotationRate.x, y: rotationRate.y, z: rotationRate.z, sink: sink) + sendFlutter( + x: rotationRate.x, + y: rotationRate.y, + z: rotationRate.z, + timestamp: data!.timestamp, + sink: sink + ) } return nil } @@ -205,7 +216,13 @@ class FPPMagnetometerStreamHandlerPlus: NSObject, MotionStreamHandler { return } let magneticField = data!.magneticField - sendTriplet(x: magneticField.x, y: magneticField.y, z: magneticField.z, sink: sink) + sendFlutter( + x: magneticField.x, + y: magneticField.y, + z: magneticField.z, + timestamp: data!.timestamp, + sink: sink + ) } return nil } diff --git a/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart b/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart index 80affefdf95..23e757f8a73 100644 --- a/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart +++ b/packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart @@ -76,6 +76,7 @@ class WebSensorsPlugin extends SensorsPlatform { accelerometer.x as double, accelerometer.y as double, accelerometer.z as double, + DateTime.now(), ), ); }, @@ -93,7 +94,8 @@ class WebSensorsPlugin extends SensorsPlatform { apiName: 'Accelerometer()', permissionName: 'accelerometer', onError: () { - _accelerometerStreamController!.add(AccelerometerEvent(0, 0, 0)); + _accelerometerStreamController! + .add(AccelerometerEvent(0, 0, 0, DateTime.now())); }, ); _accelerometerResultStream = @@ -131,6 +133,7 @@ class WebSensorsPlugin extends SensorsPlatform { gyroscope.x as double, gyroscope.y as double, gyroscope.z as double, + DateTime.now(), ), ); }, @@ -148,7 +151,8 @@ class WebSensorsPlugin extends SensorsPlatform { apiName: 'Gyroscope()', permissionName: 'gyroscope', onError: () { - _gyroscopeEventStreamController!.add(GyroscopeEvent(0, 0, 0)); + _gyroscopeEventStreamController! + .add(GyroscopeEvent(0, 0, 0, DateTime.now())); }, ); _gyroscopeEventResultStream = @@ -187,6 +191,7 @@ class WebSensorsPlugin extends SensorsPlatform { linearAccelerationSensor.x as double, linearAccelerationSensor.y as double, linearAccelerationSensor.z as double, + DateTime.now(), ), ); }, @@ -205,7 +210,7 @@ class WebSensorsPlugin extends SensorsPlatform { permissionName: 'accelerometer', onError: () { _userAccelerometerStreamController! - .add(UserAccelerometerEvent(0, 0, 0)); + .add(UserAccelerometerEvent(0, 0, 0, DateTime.now())); }, ); _userAccelerometerResultStream = @@ -243,6 +248,7 @@ class WebSensorsPlugin extends SensorsPlatform { magnetometerSensor.x as double, magnetometerSensor.y as double, magnetometerSensor.z as double, + DateTime.now(), ), ); }, @@ -260,7 +266,8 @@ class WebSensorsPlugin extends SensorsPlatform { apiName: 'Magnetometer()', permissionName: 'magnetometer', onError: () { - _magnetometerStreamController!.add(MagnetometerEvent(0, 0, 0)); + _magnetometerStreamController! + .add(MagnetometerEvent(0, 0, 0, DateTime.now())); }, ); _magnetometerResultStream = diff --git a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/accelerometer_event.dart b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/accelerometer_event.dart index 57d688a4baa..baf432db90d 100644 --- a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/accelerometer_event.dart +++ b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/accelerometer_event.dart @@ -8,7 +8,7 @@ /// a particular direction. class AccelerometerEvent { /// Constructs an instance with the given [x], [y], and [z] values. - AccelerometerEvent(this.x, this.y, this.z); + AccelerometerEvent(this.x, this.y, this.z, this.timestamp); /// Acceleration force along the x axis (including gravity) measured in m/s^2. /// @@ -30,6 +30,14 @@ class AccelerometerEvent { /// towards the user and negative mean it is moving away from them. final double z; + /// timestamp of the event + /// + /// This is the timestamp of the event in microseconds, as provided by the + /// underlying platform. For Android, this is the uptimeMillis provided by + /// the SensorEvent. For iOS, this is the timestamp provided by the CMDeviceMotion. + + final DateTime timestamp; + @override String toString() => '[AccelerometerEvent (x: $x, y: $y, z: $z)]'; } diff --git a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/gyroscope_event.dart b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/gyroscope_event.dart index 59c90e4b851..312721a19aa 100644 --- a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/gyroscope_event.dart +++ b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/gyroscope_event.dart @@ -6,7 +6,7 @@ /// the device in 3D space. class GyroscopeEvent { /// Constructs an instance with the given [x], [y], and [z] values. - GyroscopeEvent(this.x, this.y, this.z); + GyroscopeEvent(this.x, this.y, this.z, this.timestamp); /// Rate of rotation around the x axis measured in rad/s. /// @@ -30,6 +30,14 @@ class GyroscopeEvent { /// on. final double z; + /// timestamp of the event + /// + /// This is the timestamp of the event in microseconds, as provided by the + /// underlying platform. For Android, this is the uptimeMillis provided by + /// the SensorEvent. For iOS, this is the timestamp provided by the CMDeviceMotion. + + final DateTime timestamp; + @override String toString() => '[GyroscopeEvent (x: $x, y: $y, z: $z)]'; } diff --git a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/magnetometer_event.dart b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/magnetometer_event.dart index b17eef1507e..376f47c6c9b 100644 --- a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/magnetometer_event.dart +++ b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/magnetometer_event.dart @@ -12,12 +12,20 @@ class MagnetometerEvent { /// Constructs a new instance with the given [x], [y], and [z] values. /// /// See [MagnetometerEvent] for more information. - MagnetometerEvent(this.x, this.y, this.z); + MagnetometerEvent(this.x, this.y, this.z, this.timestamp); /// The ambient magnetic field in this axis surrounding the sensor in /// microteslas ***μT***. final double x, y, z; + /// timestamp of the event + /// + /// This is the timestamp of the event in microseconds, as provided by the + /// underlying platform. For Android, this is the uptimeMillis provided by + /// the SensorEvent. For iOS, this is the timestamp provided by the CMDeviceMotion. + + final DateTime timestamp; + @override String toString() => '[MagnetometerEvent (x: $x, y: $y, z: $z)]'; } diff --git a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/method_channel_sensors.dart b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/method_channel_sensors.dart index 442c5fa151a..362cc01c3a6 100644 --- a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/method_channel_sensors.dart +++ b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/method_channel_sensors.dart @@ -52,7 +52,12 @@ class MethodChannelSensors extends SensorsPlatform { .receiveBroadcastStream() .map((dynamic event) { final list = event.cast(); - return AccelerometerEvent(list[0]!, list[1]!, list[2]!); + return AccelerometerEvent( + list[0]!, + list[1]!, + list[2]!, + DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()), + ); }); return _accelerometerEvents!; } @@ -77,7 +82,12 @@ class MethodChannelSensors extends SensorsPlatform { _gyroscopeEvents ??= _gyroscopeEventChannel.receiveBroadcastStream().map((dynamic event) { final list = event.cast(); - return GyroscopeEvent(list[0]!, list[1]!, list[2]!); + return GyroscopeEvent( + list[0]!, + list[1]!, + list[2]!, + DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()), + ); }); return _gyroscopeEvents!; } @@ -104,7 +114,12 @@ class MethodChannelSensors extends SensorsPlatform { .receiveBroadcastStream() .map((dynamic event) { final list = event.cast(); - return UserAccelerometerEvent(list[0]!, list[1]!, list[2]!); + return UserAccelerometerEvent( + list[0]!, + list[1]!, + list[2]!, + DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()), + ); }); return _userAccelerometerEvents!; } @@ -129,7 +144,12 @@ class MethodChannelSensors extends SensorsPlatform { _magnetometerEvents ??= _magnetometerEventChannel.receiveBroadcastStream().map((dynamic event) { final list = event.cast(); - return MagnetometerEvent(list[0]!, list[1]!, list[2]!); + return MagnetometerEvent( + list[0]!, + list[1]!, + list[2]!, + DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()), + ); }); return _magnetometerEvents!; } diff --git a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/user_accelerometer_event.dart b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/user_accelerometer_event.dart index c8d2fa8ded9..4d8f74a2715 100644 --- a/packages/sensors_plus/sensors_plus_platform_interface/lib/src/user_accelerometer_event.dart +++ b/packages/sensors_plus/sensors_plus_platform_interface/lib/src/user_accelerometer_event.dart @@ -7,7 +7,7 @@ /// [AccelerometerEvent], this event does not include the effects of gravity. class UserAccelerometerEvent { /// Constructs an instance with the given [x], [y], and [z] values. - UserAccelerometerEvent(this.x, this.y, this.z); + UserAccelerometerEvent(this.x, this.y, this.z, this.timestamp); /// Acceleration force along the x axis (excluding gravity) measured in m/s^2. /// @@ -29,6 +29,18 @@ class UserAccelerometerEvent { /// towards the user and negative mean it is moving away from them. final double z; + /// timestamp of the event + /// + /// This is the timestamp of the event in microseconds, as provided by the + /// underlying platform. For Android, this is the timestamp in the event in + /// nanoseconds from the `android.os.SystemClock.elapsedRealtimeNanos` API, + /// and synchronized with SystemClock.uptimeMillis() to be in the same time. + /// For iOS, this is the TimeInterval in the event synchronized with + /// [NSDate timeIntervalSince1970] to be in the same time. + + final DateTime timestamp; + @override - String toString() => '[UserAccelerometerEvent (x: $x, y: $y, z: $z)]'; + String toString() => + '[UserAccelerometerEvent (x: $x, y: $y, z: $z, timestamp: $timestamp)]'; }