@@ -26,6 +26,12 @@ class GyroscopeProvider extends ChangeNotifier {
2626 bool _isRecording = false ;
2727 List <List <dynamic >> _recordedData = [];
2828
29+ bool _isPlayingBack = false ;
30+ List <List <dynamic >>? _playbackData;
31+ int _playbackIndex = 0 ;
32+ Timer ? _playbackTimer;
33+ bool _isPlaybackPaused = false ;
34+
2935 double get xValue => _gyroscopeEvent.x;
3036 double get yValue => _gyroscopeEvent.y;
3137 double get zValue => _gyroscopeEvent.z;
@@ -39,6 +45,10 @@ class GyroscopeProvider extends ChangeNotifier {
3945
4046 bool get isListening => _gyroscopeSubscription != null ;
4147 bool get isRecording => _isRecording;
48+ bool get isPlayingBack => _isPlayingBack;
49+ bool get isPlaybackPaused => _isPlaybackPaused;
50+
51+ Function ? onPlaybackEnd;
4252
4353 void initializeSensors () {
4454 if (_gyroscopeSubscription != null ) return ;
@@ -61,6 +71,116 @@ class GyroscopeProvider extends ChangeNotifier {
6171 _gyroscopeSubscription = null ;
6272 }
6373
74+ void startPlayback (List <List <dynamic >> data) {
75+ if (data.length <= 1 ) {
76+ logger.w ("Playback skipped: insufficient data (length <= 1)" );
77+ return ;
78+ }
79+
80+ _isPlayingBack = true ;
81+ _isPlaybackPaused = false ;
82+ _playbackData = data;
83+ _playbackIndex = 1 ;
84+ disposeSensors ();
85+ _xData.clear ();
86+ _yData.clear ();
87+ _zData.clear ();
88+ xData.clear ();
89+ yData.clear ();
90+ zData.clear ();
91+ _startPlaybackTimer ();
92+ notifyListeners ();
93+ }
94+
95+ void _startPlaybackTimer () {
96+ if (_playbackIndex >= _playbackData! .length) {
97+ stopPlayback ();
98+ return ;
99+ }
100+
101+ final currentRow = _playbackData! [_playbackIndex];
102+ if (currentRow.length > 4 ) {
103+ final x = double .tryParse (currentRow[2 ].toString ()) ?? 0.0 ;
104+ final y = double .tryParse (currentRow[3 ].toString ()) ?? 0.0 ;
105+ final z = double .tryParse (currentRow[4 ].toString ()) ?? 0.0 ;
106+
107+ _gyroscopeEvent = GyroscopeEvent (x, y, z, DateTime .now ());
108+ _updateData ();
109+ _playbackIndex++ ;
110+ notifyListeners ();
111+ } else {
112+ logger.e (
113+ 'Skipping playback row at index $_playbackIndex due to insufficient columns (found ${currentRow .length }, expected at least 5' );
114+ _playbackIndex++ ;
115+ notifyListeners ();
116+ }
117+
118+ Duration interval = const Duration (seconds: 1 );
119+
120+ if (_playbackIndex < _playbackData! .length && _playbackIndex > 1 ) {
121+ try {
122+ final currentTimestamp =
123+ int .tryParse (_playbackData! [_playbackIndex - 1 ][0 ].toString ());
124+ final nextTimestamp =
125+ int .tryParse (_playbackData! [_playbackIndex][0 ].toString ());
126+
127+ if (currentTimestamp != null && nextTimestamp != null ) {
128+ final timeDiff = nextTimestamp - currentTimestamp;
129+ interval = Duration (milliseconds: timeDiff);
130+ if (interval.inMilliseconds < 100 ) {
131+ interval = const Duration (milliseconds: 100 );
132+ } else if (interval.inMilliseconds > 10000 ) {
133+ interval = const Duration (seconds: 10 );
134+ }
135+ }
136+ } catch (e) {
137+ interval = const Duration (seconds: 1 );
138+ }
139+ }
140+
141+ _playbackTimer = Timer (interval, () {
142+ if (_isPlayingBack && ! _isPlaybackPaused) {
143+ _startPlaybackTimer ();
144+ }
145+ });
146+ }
147+
148+ Future <void > stopPlayback () async {
149+ _isPlayingBack = false ;
150+ _isPlaybackPaused = false ;
151+ _playbackTimer? .cancel ();
152+ _playbackData = null ;
153+ _playbackIndex = 0 ;
154+
155+ _xData.clear ();
156+ _yData.clear ();
157+ _zData.clear ();
158+ xData.clear ();
159+ yData.clear ();
160+ zData.clear ();
161+
162+ _gyroscopeEvent = GyroscopeEvent (0 , 0 , 0 , DateTime .now ());
163+
164+ notifyListeners ();
165+ onPlaybackEnd? .call ();
166+ }
167+
168+ void pausePlayback () {
169+ if (_isPlayingBack) {
170+ _isPlaybackPaused = true ;
171+ _playbackTimer? .cancel ();
172+ notifyListeners ();
173+ }
174+ }
175+
176+ void resumePlayback () {
177+ if (_isPlayingBack && _isPlaybackPaused) {
178+ _isPlaybackPaused = false ;
179+ _startPlaybackTimer ();
180+ notifyListeners ();
181+ }
182+ }
183+
64184 void _updateData () {
65185 final x = _gyroscopeEvent.x;
66186 final y = _gyroscopeEvent.y;
@@ -88,12 +208,18 @@ class GyroscopeProvider extends ChangeNotifier {
88208 if (_yData.length > _maxLength) _yData.removeAt (0 );
89209 if (_zData.length > _maxLength) _zData.removeAt (0 );
90210
91- _xMin = _xData.reduce (min);
92- _xMax = _xData.reduce (max);
93- _yMin = _yData.reduce (min);
94- _yMax = _yData.reduce (max);
95- _zMin = _zData.reduce (min);
96- _zMax = _zData.reduce (max);
211+ if (_xData.isNotEmpty) {
212+ _xMin = _xData.reduce (min);
213+ _xMax = _xData.reduce (max);
214+ }
215+ if (_yData.isNotEmpty) {
216+ _yMin = _yData.reduce (min);
217+ _yMax = _yData.reduce (max);
218+ }
219+ if (_zData.isNotEmpty) {
220+ _zMin = _zData.reduce (min);
221+ _zMax = _zData.reduce (max);
222+ }
97223
98224 xData.clear ();
99225 yData.clear ();
@@ -196,6 +322,7 @@ class GyroscopeProvider extends ChangeNotifier {
196322
197323 @override
198324 void dispose () {
325+ _playbackTimer? .cancel ();
199326 disposeSensors ();
200327 super .dispose ();
201328 }
0 commit comments