diff --git a/packages/sensors/CHANGELOG.md b/packages/sensors/CHANGELOG.md index ea488586cb87..aecf8d4c83ec 100644 --- a/packages/sensors/CHANGELOG.md +++ b/packages/sensors/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.1+3 + +* Improve documentation and add unit test coverage. + ## 0.4.1+2 * Remove AndroidX warnings. diff --git a/packages/sensors/README.md b/packages/sensors/README.md index fdd450217487..4bb9f4f254e1 100644 --- a/packages/sensors/README.md +++ b/packages/sensors/README.md @@ -5,7 +5,22 @@ A Flutter plugin to access the accelerometer and gyroscope sensors. ## Usage -To use this plugin, add `sensors` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). +To use this plugin, add `sensors` as a [dependency in your pubspec.yaml +file](https://flutter.io/platform-plugins/). + +This will expose three classes of sensor events, through three different +streams. + +- `AccelerometerEvent`s describe the velocity of the device, including the + effects of gravity. Put simply, you can use accelerometer readings to tell if + the device is moving in a particular direction. +- `UserAccelerometerEvent`s also describe the velocity of the device, but don't + include gravity. They can also be thought of as just the user's affect on the + device. +- `GyroscopeEvent`s describe the rotation of the device. + +Each of these is exposed through a `BroadcastStream`: `accelerometerEvents`, +`userAccelerometerEvents`, and `gyroscopeEvents`, respectively. ### Example @@ -14,10 +29,21 @@ To use this plugin, add `sensors` as a [dependency in your pubspec.yaml file](ht import 'package:sensors/sensors.dart'; accelerometerEvents.listen((AccelerometerEvent event) { - // Do something with the event. + print(event); }); +// [AccelerometerEvent (x: 0.0, y: 9.8, z: 0.0)] + +userAccelerometerEvents.listen((AccelerometerEvent event) { + print(event); +}); +// [UserAccelerometerEvent (x: 0.0, y: 0.0, z: 0.0)] gyroscopeEvents.listen((GyroscopeEvent event) { - // Do something with the event. + print(event); }); -``` \ No newline at end of file +// [GyroscopeEvent (x: 0.0, y: 0.0, z: 0.0)] + +``` + +Also see the `example` subdirectory for an example application that uses the +sensor data. diff --git a/packages/sensors/analysis_options.yaml b/packages/sensors/analysis_options.yaml new file mode 100644 index 000000000000..4d3c53a24cab --- /dev/null +++ b/packages/sensors/analysis_options.yaml @@ -0,0 +1,11 @@ +# This exists to add a lint for missing API docs just on this specific package, +# since not all packages have coverage for all their public members yet and +# adding it in would be non-trivial. `public_member_api_docs` should be applied +# to new packages going forward, and ideally the main `analysis_options.yaml` +# file as soon as possible. + +include: ../../analysis_options.yaml + +linter: + rules: + - public_member_api_docs diff --git a/packages/sensors/example/lib/main.dart b/packages/sensors/example/lib/main.dart index e574a64f5f38..575e0493742f 100644 --- a/packages/sensors/example/lib/main.dart +++ b/packages/sensors/example/lib/main.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'package:flutter/material.dart'; import 'package:sensors/sensors.dart'; diff --git a/packages/sensors/example/lib/snake.dart b/packages/sensors/example/lib/snake.dart index b870791618e9..2b7cad4cf2e0 100644 --- a/packages/sensors/example/lib/snake.dart +++ b/packages/sensors/example/lib/snake.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'dart:math' as math; diff --git a/packages/sensors/lib/sensors.dart b/packages/sensors/lib/sensors.dart index 7b41a4959f94..0b6f1b5a6067 100644 --- a/packages/sensors/lib/sensors.dart +++ b/packages/sensors/lib/sensors.dart @@ -14,48 +14,95 @@ const EventChannel _userAccelerometerEventChannel = const EventChannel _gyroscopeEventChannel = EventChannel('plugins.flutter.io/sensors/gyroscope'); +/// Discrete reading from an accelerometer. Accelerometers measure the velocity +/// of the device. Note that these readings include the effects of gravity. Put +/// simply, you can use accelerometer readings to tell if the device is moving in +/// a particular direction. class AccelerometerEvent { + /// Contructs an instance with the given [x], [y], and [z] values. AccelerometerEvent(this.x, this.y, this.z); /// Acceleration force along the x axis (including gravity) measured in m/s^2. + /// + /// When the device is held upright facing the user, positive values mean the + /// device is moving to the right and negative mean it is moving to the left. final double x; /// Acceleration force along the y axis (including gravity) measured in m/s^2. + /// + /// When the device is held upright facing the user, positive values mean the + /// device is moving towards the sky and negative mean it is moving towards + /// the ground. final double y; /// Acceleration force along the z axis (including gravity) measured in m/s^2. + /// + /// This uses a right-handed coordinate system. So when the device is held + /// upright and facing the user, positive values mean the device is moving + /// towards the user and negative mean it is moving away from them. final double z; @override String toString() => '[AccelerometerEvent (x: $x, y: $y, z: $z)]'; } +/// Discrete reading from a gyroscope. Gyroscopes measure the rate or rotation of +/// the device in 3D space. class GyroscopeEvent { + /// Contructs an instance with the given [x], [y], and [z] values. GyroscopeEvent(this.x, this.y, this.z); /// Rate of rotation around the x axis measured in rad/s. + /// + /// When the device is held upright, this can also be thought of as describing + /// "pitch". The top of the device will tilt towards or away from the + /// user as this value changes. final double x; /// Rate of rotation around the y axis measured in rad/s. + /// + /// When the device is held upright, this can also be thought of as describing + /// "yaw". The lengthwise edge of the device will rotate towards or away from + /// the user as this value changes. final double y; /// Rate of rotation around the z axis measured in rad/s. + /// + /// When the device is held upright, this can also be thought of as describing + /// "roll". When this changes the face of the device should remain facing + /// forward, but the orientation will change from portrait to landscape and so + /// on. final double z; @override String toString() => '[GyroscopeEvent (x: $x, y: $y, z: $z)]'; } +/// Like [AccelerometerEvent], this is a discrete reading from an accelerometer +/// and measures the velocity of the device. However, unlike +/// [AccelerometerEvent], this event does not include the effects of gravity. class UserAccelerometerEvent { + /// Contructs an instance with the given [x], [y], and [z] values. UserAccelerometerEvent(this.x, this.y, this.z); /// Acceleration force along the x axis (excluding gravity) measured in m/s^2. + /// + /// When the device is held upright facing the user, positive values mean the + /// device is moving to the right and negative mean it is moving to the left. final double x; /// Acceleration force along the y axis (excluding gravity) measured in m/s^2. + /// + /// When the device is held upright facing the user, positive values mean the + /// device is moving towards the sky and negative mean it is moving towards + /// the ground. final double y; /// Acceleration force along the z axis (excluding gravity) measured in m/s^2. + /// + /// This uses a right-handed coordinate system. So when the device is held + /// upright and facing the user, positive values mean the device is moving + /// towards the user and negative mean it is moving away from them. final double z; @override diff --git a/packages/sensors/pubspec.yaml b/packages/sensors/pubspec.yaml index 38e698299b9e..b750d93865c6 100644 --- a/packages/sensors/pubspec.yaml +++ b/packages/sensors/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for accessing the Android and iOS accelerometer and gyroscope sensors. author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/sensors -version: 0.4.1+2 +version: 0.4.1+3 flutter: plugin: @@ -20,6 +20,7 @@ dev_dependencies: flutter_test: sdk: flutter e2e: ^0.2.0 + mockito: ^4.1.1 environment: sdk: ">=2.0.0-dev.28.0 <3.0.0" diff --git a/packages/sensors/test/sensors_test.dart b/packages/sensors/test/sensors_test.dart index 603f805386fd..1485d589ad72 100644 --- a/packages/sensors/test/sensors_test.dart +++ b/packages/sensors/test/sensors_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/services.dart'; @@ -16,44 +15,67 @@ void main() { test('$accelerometerEvents are streamed', () async { const String channelName = 'plugins.flutter.io/sensors/accelerometer'; const List sensorData = [1.0, 2.0, 3.0]; + _initializeFakeSensorChannel(channelName, sensorData); - const StandardMethodCodec standardMethod = StandardMethodCodec(); - - void emitEvent(ByteData event) { - // TODO(hterkelsen): Remove this when defaultBinaryMessages is in stable. - // https://github.com/flutter/flutter/issues/33446 - // ignore: deprecated_member_use - BinaryMessages.handlePlatformMessage( - channelName, - event, - (ByteData reply) {}, - ); - } + final AccelerometerEvent event = await accelerometerEvents.first; + + expect(event.x, sensorData[0]); + expect(event.y, sensorData[1]); + expect(event.z, sensorData[2]); + }); + + test('$gyroscopeEvents are streamed', () async { + const String channelName = 'plugins.flutter.io/sensors/gyroscope'; + const List sensorData = [3.0, 4.0, 5.0]; + _initializeFakeSensorChannel(channelName, sensorData); + + final GyroscopeEvent event = await gyroscopeEvents.first; + + expect(event.x, sensorData[0]); + expect(event.y, sensorData[1]); + expect(event.z, sensorData[2]); + }); + + test('$userAccelerometerEvents are streamed', () async { + const String channelName = 'plugins.flutter.io/sensors/user_accel'; + const List sensorData = [6.0, 7.0, 8.0]; + _initializeFakeSensorChannel(channelName, sensorData); - bool isCanceled = false; + final UserAccelerometerEvent event = await userAccelerometerEvents.first; + + expect(event.x, sensorData[0]); + expect(event.y, sensorData[1]); + expect(event.z, sensorData[2]); + }); +} + +void _initializeFakeSensorChannel(String channelName, List sensorData) { + const StandardMethodCodec standardMethod = StandardMethodCodec(); + + void _emitEvent(ByteData event) { // TODO(hterkelsen): Remove this when defaultBinaryMessages is in stable. // https://github.com/flutter/flutter/issues/33446 // ignore: deprecated_member_use - BinaryMessages.setMockMessageHandler(channelName, (ByteData message) async { - final MethodCall methodCall = standardMethod.decodeMethodCall(message); - if (methodCall.method == 'listen') { - emitEvent(standardMethod.encodeSuccessEnvelope(sensorData)); - emitEvent(null); - return standardMethod.encodeSuccessEnvelope(null); - } else if (methodCall.method == 'cancel') { - isCanceled = true; - return standardMethod.encodeSuccessEnvelope(null); - } else { - fail('Expected listen or cancel'); - } - }); - - final AccelerometerEvent event = await accelerometerEvents.first; - expect(event.x, 1.0); - expect(event.y, 2.0); - expect(event.z, 3.0); + BinaryMessages.handlePlatformMessage( + channelName, + event, + (ByteData reply) {}, + ); + } - await Future.delayed(Duration.zero); - expect(isCanceled, isTrue); + // TODO(hterkelsen): Remove this when defaultBinaryMessages is in stable. + // https://github.com/flutter/flutter/issues/33446 + // ignore: deprecated_member_use + BinaryMessages.setMockMessageHandler(channelName, (ByteData message) async { + final MethodCall methodCall = standardMethod.decodeMethodCall(message); + if (methodCall.method == 'listen') { + _emitEvent(standardMethod.encodeSuccessEnvelope(sensorData)); + _emitEvent(null); + return standardMethod.encodeSuccessEnvelope(null); + } else if (methodCall.method == 'cancel') { + return standardMethod.encodeSuccessEnvelope(null); + } else { + fail('Expected listen or cancel'); + } }); }