@@ -26,12 +26,20 @@ class AccelerometerStateProvider extends ChangeNotifier {
2626 double _yMin = 0 , _yMax = 0 ;
2727 double _zMin = 0 , _zMax = 0 ;
2828 bool _isRecording = false ;
29+ bool _isPlayingBack = false ;
30+ List <List <dynamic >>? _playbackData;
31+ int _playbackIndex = 0 ;
32+ Timer ? _playbackTimer;
33+ bool _isPlaybackPaused = false ;
2934 List <List <dynamic >> _recordedData = [];
3035 bool get isRecording => _isRecording;
36+ bool get isPlayingBack => _isPlayingBack;
37+ bool get isPlaybackPaused => _isPlaybackPaused;
38+
3139 late AccelerometerConfigProvider _configProvider;
3240 StreamSubscription ? _locationStream;
33-
3441 Position ? currentPosition;
42+ Function ? onPlaybackEnd;
3543
3644 void setConfigProvider (AccelerometerConfigProvider configProvider) {
3745 _configProvider = configProvider;
@@ -40,37 +48,36 @@ class AccelerometerStateProvider extends ChangeNotifier {
4048 AccelerometerConfigProvider ? get configProvider => _configProvider;
4149
4250 Future <void > _startGeoLocationUpdates () async {
43- bool serviceEnabled;
44- LocationPermission permission;
45-
46- serviceEnabled = await Geolocator .isLocationServiceEnabled ();
47- if (! serviceEnabled) {
48- logger.w ('Location services are disabled.' );
49- return ;
50- }
51+ try {
52+ bool serviceEnabled = await Geolocator .isLocationServiceEnabled ();
53+ if (! serviceEnabled) return ;
5154
52- permission = await Geolocator .checkPermission ();
53- if (permission == LocationPermission .denied) {
54- permission = await Geolocator .requestPermission ();
55+ LocationPermission permission = await Geolocator .checkPermission ();
5556 if (permission == LocationPermission .denied) {
56- logger. w ( 'Location permissions are denied' );
57- return ;
57+ permission = await Geolocator . requestPermission ( );
58+ if (permission == LocationPermission .denied) return ;
5859 }
59- }
60+ if (permission == LocationPermission .deniedForever) return ;
6061
61- if (permission == LocationPermission .deniedForever) {
62- logger.w (
63- 'Location permissions are permanently denied, we cannot request permissions.' );
64- return ;
65- }
62+ await _locationStream? .cancel ();
6663
67- _locationStream = Geolocator .getPositionStream (
68- locationSettings: const LocationSettings (
69- accuracy: LocationAccuracy .high,
70- ),
71- ).listen ((Position position) {
72- currentPosition = position;
73- });
64+ const locationSettings = LocationSettings (
65+ accuracy: LocationAccuracy .medium,
66+ distanceFilter: 5 ,
67+ );
68+
69+ _locationStream = Stream .periodic (const Duration (seconds: 6 ))
70+ .asyncMap (
71+ (_) => Geolocator .getCurrentPosition (
72+ locationSettings: locationSettings,
73+ ),
74+ )
75+ .listen ((Position position) {
76+ currentPosition = position;
77+ });
78+ } catch (e) {
79+ logger.e ('Error starting location updates: $e ' );
80+ }
7481 }
7582
7683 void initializeSensors () {
@@ -81,16 +88,15 @@ class AccelerometerStateProvider extends ChangeNotifier {
8188 notifyListeners ();
8289 },
8390 onError: (error) {
84- logger.e (
85- "Accelerometer error: $error " ,
86- );
91+ logger.e ("Accelerometer error: $error " );
8792 },
8893 cancelOnError: true ,
8994 );
9095 }
9196
9297 void disposeSensors () {
9398 _accelerometerSubscription? .cancel ();
99+ _playbackTimer? .cancel ();
94100 }
95101
96102 @override
@@ -102,6 +108,109 @@ class AccelerometerStateProvider extends ChangeNotifier {
102108 super .dispose ();
103109 }
104110
111+ Future <void > startPlayback (List <List <dynamic >> data) async {
112+ if (data.length <= 1 ) return ;
113+ _isPlayingBack = true ;
114+ _isPlaybackPaused = false ;
115+ _playbackTimer? .cancel ();
116+ _playbackData = data;
117+ _playbackIndex = 1 ;
118+ disposeSensors ();
119+ _xData.clear ();
120+ _yData.clear ();
121+ _zData.clear ();
122+ xData.clear ();
123+ yData.clear ();
124+ zData.clear ();
125+ _startPlaybackTimer ();
126+ notifyListeners ();
127+ }
128+
129+ void _startPlaybackTimer () {
130+ if (_playbackIndex >= _playbackData! .length) {
131+ stopPlayback ();
132+ return ;
133+ }
134+
135+ final currentRow = _playbackData! [_playbackIndex];
136+ if (currentRow.length > 4 ) {
137+ final x = double .tryParse (currentRow[2 ].toString ()) ?? 0.0 ;
138+ final y = double .tryParse (currentRow[3 ].toString ()) ?? 0.0 ;
139+ final z = double .tryParse (currentRow[4 ].toString ()) ?? 0.0 ;
140+
141+ _accelerometerEvent = AccelerometerEvent (x, y, z, DateTime .now ());
142+ _updateData ();
143+ _playbackIndex++ ;
144+ notifyListeners ();
145+ } else {
146+ logger.e (
147+ 'Skipping playback row at index $_playbackIndex due to insufficient columns (found ${currentRow .length }, expected at least 5' );
148+ _playbackIndex++ ;
149+ notifyListeners ();
150+ }
151+
152+ Duration interval = const Duration (seconds: 1 );
153+
154+ if (_playbackIndex < _playbackData! .length && _playbackIndex > 1 ) {
155+ try {
156+ final currentTimestamp =
157+ int .tryParse (_playbackData! [_playbackIndex - 1 ][0 ].toString ());
158+ final nextTimestamp =
159+ int .tryParse (_playbackData! [_playbackIndex][0 ].toString ());
160+
161+ if (currentTimestamp != null && nextTimestamp != null ) {
162+ final timeDiff = nextTimestamp - currentTimestamp;
163+ interval = Duration (milliseconds: timeDiff);
164+ if (interval.inMilliseconds < 100 ) {
165+ interval = const Duration (milliseconds: 100 );
166+ } else if (interval.inMilliseconds > 10000 ) {
167+ interval = const Duration (seconds: 10 );
168+ }
169+ }
170+ } catch (e) {
171+ interval = const Duration (seconds: 1 );
172+ }
173+ }
174+
175+ _playbackTimer = Timer (interval, () {
176+ if (_isPlayingBack && ! _isPlaybackPaused) {
177+ _startPlaybackTimer ();
178+ }
179+ });
180+ }
181+
182+ Future <void > stopPlayback () async {
183+ _isPlayingBack = false ;
184+ _isPlaybackPaused = false ;
185+ _playbackData = null ;
186+ _playbackIndex = 0 ;
187+ _xData.clear ();
188+ _yData.clear ();
189+ _zData.clear ();
190+ xData.clear ();
191+ yData.clear ();
192+ zData.clear ();
193+ _accelerometerEvent = AccelerometerEvent (0 , 0 , 0 , DateTime .now ());
194+ notifyListeners ();
195+ onPlaybackEnd? .call ();
196+ }
197+
198+ void pausePlayback () {
199+ if (_isPlayingBack) {
200+ _isPlaybackPaused = true ;
201+ _playbackTimer? .cancel ();
202+ notifyListeners ();
203+ }
204+ }
205+
206+ void resumePlayback () {
207+ if (_isPlayingBack && _isPlaybackPaused) {
208+ _isPlaybackPaused = false ;
209+ _startPlaybackTimer ();
210+ notifyListeners ();
211+ }
212+ }
213+
105214 void _updateData () {
106215 final x = _accelerometerEvent.x;
107216 final y = _accelerometerEvent.y;
0 commit comments