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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
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 @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)))
}
Expand Down Expand Up @@ -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
)
}
Expand Down Expand Up @@ -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
)
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
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 @@ -76,6 +76,7 @@ class WebSensorsPlugin extends SensorsPlatform {
accelerometer.x as double,
accelerometer.y as double,
accelerometer.z as double,
DateTime.now(),
),
);
},
Expand All @@ -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 =
Expand Down Expand Up @@ -131,6 +133,7 @@ class WebSensorsPlugin extends SensorsPlatform {
gyroscope.x as double,
gyroscope.y as double,
gyroscope.z as double,
DateTime.now(),
),
);
},
Expand All @@ -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 =
Expand Down Expand Up @@ -187,6 +191,7 @@ class WebSensorsPlugin extends SensorsPlatform {
linearAccelerationSensor.x as double,
linearAccelerationSensor.y as double,
linearAccelerationSensor.z as double,
DateTime.now(),
),
);
},
Expand All @@ -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 =
Expand Down Expand Up @@ -243,6 +248,7 @@ class WebSensorsPlugin extends SensorsPlatform {
magnetometerSensor.x as double,
magnetometerSensor.y as double,
magnetometerSensor.z as double,
DateTime.now(),
),
);
},
Expand All @@ -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 =
Expand Down
36 changes: 32 additions & 4 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,6 +88,7 @@ void main() {
expect(event.x, sensorData[0]);
expect(event.y, sensorData[1]);
expect(event.z, sensorData[2]);
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,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)]';
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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)]';
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)]';
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@ class MethodChannelSensors extends SensorsPlatform {
.receiveBroadcastStream()
.map((dynamic event) {
final list = event.cast<double>();
return AccelerometerEvent(list[0]!, list[1]!, list[2]!);
return AccelerometerEvent(
list[0]!,
list[1]!,
list[2]!,
DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()),
);
});
return _accelerometerEvents!;
}
Expand All @@ -77,7 +82,12 @@ class MethodChannelSensors extends SensorsPlatform {
_gyroscopeEvents ??=
_gyroscopeEventChannel.receiveBroadcastStream().map((dynamic event) {
final list = event.cast<double>();
return GyroscopeEvent(list[0]!, list[1]!, list[2]!);
return GyroscopeEvent(
list[0]!,
list[1]!,
list[2]!,
DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()),
);
});
return _gyroscopeEvents!;
}
Expand All @@ -104,7 +114,12 @@ class MethodChannelSensors extends SensorsPlatform {
.receiveBroadcastStream()
.map((dynamic event) {
final list = event.cast<double>();
return UserAccelerometerEvent(list[0]!, list[1]!, list[2]!);
return UserAccelerometerEvent(
list[0]!,
list[1]!,
list[2]!,
DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()),
);
});
return _userAccelerometerEvents!;
}
Expand All @@ -129,7 +144,12 @@ class MethodChannelSensors extends SensorsPlatform {
_magnetometerEvents ??=
_magnetometerEventChannel.receiveBroadcastStream().map((dynamic event) {
final list = event.cast<double>();
return MagnetometerEvent(list[0]!, list[1]!, list[2]!);
return MagnetometerEvent(
list[0]!,
list[1]!,
list[2]!,
DateTime.fromMicrosecondsSinceEpoch(list[3]!.toInt()),
);
});
return _magnetometerEvents!;
}
Expand Down