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

feat(sensors_plus)!: Add support for platform timestamp in event #2506

Merged
merged 5 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -65,10 +68,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)
}
}
Expand Down
10 changes: 5 additions & 5 deletions packages/sensors_plus/sensors_plus/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ class _MyHomePageState extends State<MyHomePage> {
_streamSubscriptions.add(
userAccelerometerEventStream(samplingPeriod: sensorInterval).listen(
(UserAccelerometerEvent event) {
final now = DateTime.now();
final now = event.timestamp;
setState(() {
_userAccelerometerEvent = event;
if (_userAccelerometerUpdateTime != null) {
Expand Down Expand Up @@ -296,7 +296,7 @@ class _MyHomePageState extends State<MyHomePage> {
_streamSubscriptions.add(
accelerometerEventStream(samplingPeriod: sensorInterval).listen(
(AccelerometerEvent event) {
final now = DateTime.now();
final now = event.timestamp;
setState(() {
_accelerometerEvent = event;
if (_accelerometerUpdateTime != null) {
Expand Down Expand Up @@ -325,7 +325,7 @@ class _MyHomePageState extends State<MyHomePage> {
_streamSubscriptions.add(
gyroscopeEventStream(samplingPeriod: sensorInterval).listen(
(GyroscopeEvent event) {
final now = DateTime.now();
final now = event.timestamp;
setState(() {
_gyroscopeEvent = event;
if (_gyroscopeUpdateTime != null) {
Expand Down Expand Up @@ -354,7 +354,7 @@ class _MyHomePageState extends State<MyHomePage> {
_streamSubscriptions.add(
magnetometerEventStream(samplingPeriod: sensorInterval).listen(
(MagnetometerEvent event) {
final now = DateTime.now();
final now = event.timestamp;
setState(() {
_magnetometerEvent = event;
if (_magnetometerUpdateTime != null) {
Expand Down Expand Up @@ -383,7 +383,7 @@ class _MyHomePageState extends State<MyHomePage> {
_streamSubscriptions.add(
barometerEventStream(samplingPeriod: sensorInterval).listen(
(BarometerEvent event) {
final now = DateTime.now();
final now = event.timestamp;
setState(() {
_barometerEvent = event;
if (_barometerUpdateTime != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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()
Expand All @@ -31,14 +33,15 @@ func _initAltimeter() {
}
}

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)))
}
Expand Down Expand Up @@ -74,10 +77,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
)
}
Expand Down Expand Up @@ -123,10 +127,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
)
}
Expand Down Expand Up @@ -170,7 +175,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
}
Expand Down Expand Up @@ -212,7 +223,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
}
Expand Down Expand Up @@ -257,7 +274,8 @@ class FPPBarometerStreamHandlerPlus: NSObject, MotionStreamHandler {
}
let pressure = data!.pressure.doubleValue * 10.0 // kPa to hPa (hectopascals)
DispatchQueue.main.async {
let pressureArray: [Double] = [pressure]
let timestampSince1970Micro = timestampMicroAtBoot + (data!.timestamp * 1000000)
let pressureArray: [Double] = [pressure, timestampSince1970Micro]
pressureArray.withUnsafeBufferPointer { buffer in
sink(FlutterStandardTypedData.init(float64: Data(buffer: buffer)))
}
Expand Down
15 changes: 11 additions & 4 deletions packages/sensors_plus/sensors_plus/lib/src/web_sensors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class WebSensorsPlugin extends SensorsPlatform {
accelerometer.x,
accelerometer.y,
accelerometer.z,
DateTime.now(),
),
);
}.toJS;
Expand All @@ -99,7 +100,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 =
Expand Down Expand Up @@ -138,6 +140,7 @@ class WebSensorsPlugin extends SensorsPlatform {
gyroscope.x,
gyroscope.y,
gyroscope.z,
DateTime.now(),
),
);
}.toJS;
Expand All @@ -152,7 +155,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 =
Expand Down Expand Up @@ -192,6 +196,7 @@ class WebSensorsPlugin extends SensorsPlatform {
linearAccelerationSensor.x,
linearAccelerationSensor.y,
linearAccelerationSensor.z,
DateTime.now(),
),
);
}.toJS;
Expand All @@ -207,7 +212,7 @@ class WebSensorsPlugin extends SensorsPlatform {
permissionName: 'accelerometer',
onError: () {
_userAccelerometerStreamController!
.add(UserAccelerometerEvent(0, 0, 0));
.add(UserAccelerometerEvent(0, 0, 0, DateTime.now()));
},
);
_userAccelerometerResultStream =
Expand Down Expand Up @@ -247,6 +252,7 @@ class WebSensorsPlugin extends SensorsPlatform {
magnetometerSensor.x,
magnetometerSensor.y,
magnetometerSensor.z,
DateTime.now(),
),
);
}.toJS;
Expand All @@ -261,7 +267,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 =
Expand Down
43 changes: 38 additions & 5 deletions packages/sensors_plus/sensors_plus/test/sensors_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ void main() {

test('accelerometerEvents are streamed', () async {
const channelName = 'dev.fluttercommunity.plus/sensors/accelerometer';
const sensorData = <double>[1.0, 2.0, 3.0];
final date = DateTime.now();
final sensorData = <double>[
1.0,
2.0,
3.0,
date.microsecondsSinceEpoch.toDouble(),
];
_initializeFakeMethodChannel('setAccelerationSamplingPeriod');
_initializeFakeSensorChannel(channelName, sensorData);

Expand All @@ -22,11 +28,18 @@ void main() {
expect(event.x, sensorData[0]);
expect(event.y, sensorData[1]);
expect(event.z, sensorData[2]);
expect(event.timestamp, date);
});

test('gyroscopeEvents are streamed', () async {
const channelName = 'dev.fluttercommunity.plus/sensors/gyroscope';
const sensorData = <double>[3.0, 4.0, 5.0];
final date = DateTime.now();
final sensorData = <double>[
3.0,
4.0,
5.0,
date.microsecondsSinceEpoch.toDouble(),
];
_initializeFakeMethodChannel('setGyroscopeSamplingPeriod');
_initializeFakeSensorChannel(channelName, sensorData);

Expand All @@ -35,11 +48,18 @@ void main() {
expect(event.x, sensorData[0]);
expect(event.y, sensorData[1]);
expect(event.z, sensorData[2]);
expect(event.timestamp, date);
});

test('userAccelerometerEvents are streamed', () async {
const channelName = 'dev.fluttercommunity.plus/sensors/user_accel';
const sensorData = <double>[6.0, 7.0, 8.0];
final date = DateTime.now();
final sensorData = <double>[
6.0,
7.0,
8.0,
date.microsecondsSinceEpoch.toDouble(),
];
_initializeFakeMethodChannel('setUserAccelerometerSamplingPeriod');
_initializeFakeSensorChannel(channelName, sensorData);

Expand All @@ -48,11 +68,18 @@ void main() {
expect(event.x, sensorData[0]);
expect(event.y, sensorData[1]);
expect(event.z, sensorData[2]);
expect(event.timestamp, date);
});

test('magnetometerEvents are streamed', () async {
const channelName = 'dev.fluttercommunity.plus/sensors/magnetometer';
const sensorData = <double>[8.0, 9.0, 10.0];
final date = DateTime.now();
final sensorData = <double>[
8.0,
9.0,
10.0,
date.microsecondsSinceEpoch.toDouble(),
];
_initializeFakeMethodChannel('setMagnetometerSamplingPeriod');
_initializeFakeSensorChannel(channelName, sensorData);

Expand All @@ -61,17 +88,23 @@ void main() {
expect(event.x, sensorData[0]);
expect(event.y, sensorData[1]);
expect(event.z, sensorData[2]);
expect(event.timestamp, date);
});

test('barometerEvents are streamed', () async {
const channelName = 'dev.fluttercommunity.plus/sensors/barometer';
const sensorData = <double>[1000.0];
final date = DateTime.now();
final sensorData = <double>[
1000.0,
date.microsecondsSinceEpoch.toDouble(),
];
_initializeFakeMethodChannel('setBarometerSamplingPeriod');
_initializeFakeSensorChannel(channelName, sensorData);

final event = await barometerEventStream().first;

expect(event.pressure, sensorData[0]);
expect(event.timestamp, date);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -30,6 +30,15 @@ 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)]';
String toString() =>
'[AccelerometerEvent (x: $x, y: $y, z: $z, timestamp: $timestamp)]';
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,20 @@ class BarometerEvent {
/// Constructs a new instance with the given [pressure] value.
///
/// See [BarometerEvent] for more information.
BarometerEvent(this.pressure);
BarometerEvent(this.pressure, this.timestamp);

/// The atmospheric pressure surrounding the sensor in hectopascals ***hPa***.
final double pressure;

/// 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() => '[BarometerEvent (pressure: $pressure hPa)]';
String toString() =>
'[BarometerEvent (pressure: $pressure hPa, timestamp: $timestamp)]';
}
Loading
Loading