pedometer.dart 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import 'dart:async';
  2. import 'package:flutter/services.dart';
  3. import 'dart:io' show Platform;
  4. const int _stopped = 0, _walking = 1;
  5. class Pedometer {
  6. static const EventChannel _stepDetectionChannel =
  7. const EventChannel('step_detection');
  8. static const EventChannel _stepCountChannel =
  9. const EventChannel('step_count');
  10. static StreamController<PedestrianStatus> _androidPedestrianController =
  11. StreamController.broadcast();
  12. /// Returns one step at a time.
  13. /// Events come every time a step is detected.
  14. static Stream<PedestrianStatus> get pedestrianStatusStream {
  15. Stream<PedestrianStatus> stream = _stepDetectionChannel
  16. .receiveBroadcastStream()
  17. .map((event) => PedestrianStatus._(event));
  18. if (Platform.isAndroid) return _androidStream(stream);
  19. return stream;
  20. }
  21. /// Transformed stream for the Android platform
  22. static Stream<PedestrianStatus> _androidStream(
  23. Stream<PedestrianStatus> stream) {
  24. /// Init a timer and a status
  25. Timer? t;
  26. int? pedestrianStatus;
  27. /// Listen for events on the original stream
  28. /// Transform these events by using the timer
  29. stream.listen((dynamic e) {
  30. /// If an event is received it means the status is 'walking'
  31. /// If the timer has been started, it should be cancelled
  32. /// to prevent sending out additional 'walking' events
  33. if (t != null) {
  34. t!.cancel();
  35. /// If a previous status was either not set yet, or was 'stopped'
  36. /// then a 'walking' event should be emitted.
  37. if (pedestrianStatus == null || pedestrianStatus == _stopped) {
  38. _androidPedestrianController.add(PedestrianStatus._(_walking));
  39. pedestrianStatus = _walking;
  40. }
  41. }
  42. /// After receiving an event, start a timer for 2 seconds, after
  43. /// which a 'stopped' event is emitted. If it manages to go through,
  44. /// it is because no events were received for the 2 second duration
  45. t = Timer(Duration(seconds: 2), () {
  46. _androidPedestrianController.add(PedestrianStatus._(_stopped));
  47. pedestrianStatus = _stopped;
  48. });
  49. });
  50. return _androidPedestrianController.stream;
  51. }
  52. /// Returns the steps taken since last system boot.
  53. /// Events may come with a delay.
  54. static Stream<StepCount> get stepCountStream => _stepCountChannel
  55. .receiveBroadcastStream()
  56. .map((event) => StepCount._(event));
  57. }
  58. /// A DTO for steps taken containing the number of steps taken.
  59. class StepCount {
  60. late DateTime _timeStamp;
  61. int _steps = 0;
  62. StepCount._(dynamic e) {
  63. _steps = e as int;
  64. _timeStamp = DateTime.now();
  65. }
  66. int get steps => _steps;
  67. DateTime get timeStamp => _timeStamp;
  68. @override
  69. String toString() =>
  70. 'Steps taken: $_steps at ${_timeStamp.toIso8601String()}';
  71. }
  72. /// A DTO for steps taken containing a detected step and its corresponding
  73. /// status, i.e. walking, stopped or unknown.
  74. class PedestrianStatus {
  75. static const _WALKING = 'walking';
  76. static const _STOPPED = 'stopped';
  77. static const _UNKNOWN = 'unknown';
  78. static const Map<int, String> _STATUSES = {
  79. _stopped: _STOPPED,
  80. _walking: _WALKING
  81. };
  82. late DateTime _timeStamp;
  83. String _status = _UNKNOWN;
  84. PedestrianStatus._(dynamic t) {
  85. int _type = t as int;
  86. _status = _STATUSES[_type]!;
  87. _timeStamp = DateTime.now();
  88. }
  89. String get status => _status;
  90. DateTime get timeStamp => _timeStamp;
  91. @override
  92. String toString() => 'Status: $_status at ${_timeStamp.toIso8601String()}';
  93. }