Skip to content

Commit 023a9f2

Browse files
rahul31124marcnause
authored andcommitted
accelerometer playback
1 parent 377686b commit 023a9f2

File tree

3 files changed

+183
-38
lines changed

3 files changed

+183
-38
lines changed

lib/providers/accelerometer_state_provider.dart

Lines changed: 139 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

lib/view/accelerometer_screen.dart

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import '../theme/colors.dart';
1616
import 'accelerometer_config_screen.dart';
1717

1818
class AccelerometerScreen extends StatefulWidget {
19-
const AccelerometerScreen({super.key});
19+
final List<List<dynamic>>? playbackData;
20+
const AccelerometerScreen({super.key, this.playbackData});
2021

2122
@override
2223
State<StatefulWidget> createState() => _AccelerometerScreenState();
@@ -35,10 +36,19 @@ class _AccelerometerScreenState extends State<AccelerometerScreen> {
3536
super.initState();
3637
_provider = AccelerometerStateProvider();
3738
_configProvider = AccelerometerConfigProvider();
39+
_provider.onPlaybackEnd = () {
40+
if (mounted && Navigator.canPop(context)) {
41+
Navigator.pop(context);
42+
}
43+
};
3844
WidgetsBinding.instance.addPostFrameCallback((_) {
3945
if (mounted) {
40-
_provider.setConfigProvider(_configProvider);
41-
_provider.initializeSensors();
46+
if (widget.playbackData != null) {
47+
_provider.startPlayback(widget.playbackData!);
48+
} else {
49+
_provider.setConfigProvider(_configProvider);
50+
_provider.initializeSensors();
51+
}
4252
}
4353
});
4454
}
@@ -235,12 +245,26 @@ class _AccelerometerScreenState extends State<AccelerometerScreen> {
235245
Consumer<AccelerometerStateProvider>(
236246
builder: (context, provider, child) {
237247
return CommonScaffold(
238-
title: appLocalizations.accelerometerTitle,
239-
key: const Key(accelerometerScreenTitleKey),
248+
title: provider.isPlayingBack
249+
? '${appLocalizations.accelerometerTitle} - ${appLocalizations.playback}'
250+
: appLocalizations.accelerometerTitle,
240251
onGuidePressed: _showInstrumentGuide,
241-
onOptionsPressed: _showOptionsMenu,
242-
onRecordPressed: _toggleRecording,
252+
onOptionsPressed:
253+
provider.isPlayingBack ? null : _showOptionsMenu,
254+
onRecordPressed: provider.isPlayingBack ? null : _toggleRecording,
243255
isRecording: provider.isRecording,
256+
isPlayingBack: provider.isPlayingBack,
257+
isPlaybackPaused: provider.isPlaybackPaused,
258+
onPlaybackPauseResume: provider.isPlayingBack
259+
? (provider.isPlaybackPaused
260+
? _provider.resumePlayback
261+
: _provider.pausePlayback)
262+
: null,
263+
onPlaybackStop: provider.isPlayingBack
264+
? () async {
265+
await _provider.stopPlayback();
266+
}
267+
: null,
244268
body: SafeArea(
245269
child: Column(
246270
children: [

lib/view/logged_data_screen.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:pslab/view/soundmeter_screen.dart';
1616
import 'package:pslab/view/wave_generator_screen.dart';
1717
import '../l10n/app_localizations.dart';
1818
import '../providers/locator.dart';
19+
import 'accelerometer_screen.dart';
1920

2021
class LoggedDataScreen extends StatefulWidget {
2122
final List<String> instrumentNames;
@@ -191,7 +192,7 @@ class _LoggedDataScreenState extends State<LoggedDataScreen> {
191192
final data = await _csvService.readCsvFromFile(file);
192193
if (data.isNotEmpty && mounted) {
193194
switch (instrumentName) {
194-
case 'soundmeter':
195+
case 'sound meter':
195196
Navigator.push(
196197
context,
197198
MaterialPageRoute(
@@ -262,6 +263,14 @@ class _LoggedDataScreenState extends State<LoggedDataScreen> {
262263
),
263264
);
264265
break;
266+
case 'accelerometer':
267+
Navigator.push(
268+
context,
269+
MaterialPageRoute(
270+
builder: (context) => AccelerometerScreen(playbackData: data),
271+
),
272+
);
273+
break;
265274
}
266275
}
267276
}
@@ -461,6 +470,9 @@ class _LoggedDataScreenState extends State<LoggedDataScreen> {
461470
.toLowerCase() ||
462471
instrumentName ==
463472
appLocalizations.logicAnalyzer
473+
.toLowerCase() ||
474+
instrumentName ==
475+
appLocalizations.accelerometer
464476
.toLowerCase())
465477
PopupMenuItem<String>(
466478
value: appLocalizations.play,

0 commit comments

Comments
 (0)