import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../bluetooth/bt_poller.dart'; import '../models/units.dart'; /// Polling choices offered in Settings. Never faster than 10 Hz: the adapter /// MCU must have time to read the ECU between requests (device maker's /// feedback). The poller additionally never sends a new request before the /// previous one is answered or has timed out. const List pollingIntervalOptionsMs = [100, 150, 200, 300]; class AppSettings { final EcuType ecuType; final Duration pollingInterval; const AppSettings({ this.ecuType = EcuType.s300, this.pollingInterval = BtPoller.defaultInterval, }); AppSettings copyWith({EcuType? ecuType, Duration? pollingInterval}) => AppSettings( ecuType: ecuType ?? this.ecuType, pollingInterval: pollingInterval ?? this.pollingInterval, ); } const _ecuTypeKey = 'ecuType'; const _pollingKey = 'pollingIntervalMs'; class SettingsNotifier extends StateNotifier { SettingsNotifier() : super(const AppSettings()) { _load(); } Future _load() async { try { final prefs = await SharedPreferences.getInstance(); final saved = prefs.getString(_ecuTypeKey); final ms = prefs.getInt(_pollingKey); var next = state; if (saved == EcuType.kpro.name) next = next.copyWith(ecuType: EcuType.kpro); if (ms != null && pollingIntervalOptionsMs.contains(ms)) { next = next.copyWith(pollingInterval: Duration(milliseconds: ms)); } if (mounted) state = next; } catch (_) { // Keep defaults if preferences can't be read. } } void setEcuType(EcuType type) { state = state.copyWith(ecuType: type); SharedPreferences.getInstance() .then((prefs) => prefs.setString(_ecuTypeKey, type.name)) .catchError((Object _) => false); } void setPollingInterval(Duration d) { final ms = pollingIntervalOptionsMs.contains(d.inMilliseconds) ? d.inMilliseconds : BtPoller.defaultInterval.inMilliseconds; state = state.copyWith(pollingInterval: Duration(milliseconds: ms)); SharedPreferences.getInstance() .then((prefs) => prefs.setInt(_pollingKey, ms)) .catchError((Object _) => false); } } final settingsProvider = StateNotifierProvider( (ref) => SettingsNotifier(), ); // ─── Display units ─────────────────────────────────────────────────────────── const _unitKeys = (speed: 'unitSpeed', temp: 'unitTemp', pressure: 'unitPressure'); T? _byName(List values, String? name) { for (final v in values) { if (v.name == name) return v; } return null; } /// Display units (speed / temperature / pressure), persisted. class UnitPrefsNotifier extends StateNotifier { UnitPrefsNotifier() : super(const UnitPrefs()) { _load(); } Future _load() async { try { final prefs = await SharedPreferences.getInstance(); final next = UnitPrefs( speed: _byName(SpeedUnit.values, prefs.getString(_unitKeys.speed)) ?? state.speed, temp: _byName(TempUnit.values, prefs.getString(_unitKeys.temp)) ?? state.temp, pressure: _byName( PressureUnit.values, prefs.getString(_unitKeys.pressure)) ?? state.pressure, ); if (mounted) state = next; } catch (_) { // Keep defaults if preferences can't be read. } } void _save(String key, Enum v) { SharedPreferences.getInstance() .then((prefs) => prefs.setString(key, v.name)) .catchError((Object _) => false); } void setSpeed(SpeedUnit v) { state = state.copyWith(speed: v); _save(_unitKeys.speed, v); } void setTemp(TempUnit v) { state = state.copyWith(temp: v); _save(_unitKeys.temp, v); } void setPressure(PressureUnit v) { state = state.copyWith(pressure: v); _save(_unitKeys.pressure, v); } } final unitPrefsProvider = StateNotifierProvider( (ref) => UnitPrefsNotifier(), );