@@ -10,16 +10,19 @@ import com.eried.eucplanet.data.repository.MetricSample
1010import com.eried.eucplanet.data.repository.SettingsRepository
1111import com.eried.eucplanet.data.repository.TripRepository
1212import com.eried.eucplanet.data.repository.WheelRepository
13+ import com.eried.eucplanet.flic.FlicManager
1314import com.eried.eucplanet.service.AutomationManager
1415import com.eried.eucplanet.service.VoiceService
1516import com.eried.eucplanet.service.WheelService
1617import dagger.hilt.android.lifecycle.HiltViewModel
1718import dagger.hilt.android.qualifiers.ApplicationContext
19+ import kotlinx.coroutines.Dispatchers
1820import kotlinx.coroutines.flow.SharingStarted
1921import kotlinx.coroutines.flow.StateFlow
2022import kotlinx.coroutines.flow.map
2123import kotlinx.coroutines.flow.stateIn
2224import kotlinx.coroutines.launch
25+ import kotlinx.coroutines.runBlocking
2326import javax.inject.Inject
2427
2528data class MetricHistory (
@@ -38,13 +41,19 @@ class DashboardViewModel @Inject constructor(
3841 private val tripRepository : TripRepository ,
3942 private val voiceService : VoiceService ,
4043 private val automationManager : AutomationManager ,
44+ private val flicManager : FlicManager ,
4145 @ApplicationContext private val context : Context
4246) : ViewModel() {
4347
4448 companion object {
4549 private const val SPARKLINE_SIZE = 300 // 5 minutes at 1 sample/sec
4650 }
4751
52+ // Synchronous initial settings read so StateFlows start with the user's persisted values
53+ // instead of hardcoded defaults (prevents a visible flash on app open).
54+ private val initialSettings: com.eried.eucplanet.data.model.AppSettings =
55+ runBlocking(Dispatchers .IO ) { settingsRepository.get() }
56+
4857 val wheelData: StateFlow < com.eried.eucplanet.data.model.WheelData > = wheelRepository.wheelData
4958
5059 val connectionState: StateFlow <ConnectionState > = wheelRepository.connectionState
@@ -58,21 +67,67 @@ class DashboardViewModel @Inject constructor(
5867 val tripCount: StateFlow <Int > = tripRepository.tripCount
5968 .stateIn(viewModelScope, SharingStarted .WhileSubscribed (5000 ), 0 )
6069
70+ // Newest trip id (first row of trips sorted by startTime DESC), or null if none.
71+ val latestTripId: StateFlow <Long ?> = tripRepository.allTrips
72+ .map { it.firstOrNull()?.id }
73+ .stateIn(viewModelScope, SharingStarted .WhileSubscribed (5000 ), null )
74+
75+ val currentTripId: StateFlow <Long ?> = tripRepository.currentTripId
76+
6177 val tiltbackSpeed: StateFlow <Float > = settingsRepository.settings
6278 .map { it.tiltbackSpeedKmh }
63- .stateIn(viewModelScope, SharingStarted .WhileSubscribed ( 5000 ), 50f )
79+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.tiltbackSpeedKmh )
6480
6581 val safetyTiltbackSpeed: StateFlow <Float > = settingsRepository.settings
6682 .map { it.safetyTiltbackKmh }
67- .stateIn(viewModelScope, SharingStarted .WhileSubscribed ( 5000 ), 25f )
83+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.safetyTiltbackKmh )
6884
6985 val imperialUnits: StateFlow <Boolean > = settingsRepository.settings
7086 .map { it.imperialUnits }
71- .stateIn(viewModelScope, SharingStarted .WhileSubscribed ( 5000 ), false )
87+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.imperialUnits )
7288
7389 val accentKey: StateFlow <String > = settingsRepository.settings
7490 .map { it.accentColor }
75- .stateIn(viewModelScope, SharingStarted .WhileSubscribed (5000 ), com.eried.eucplanet.ui.theme.AccentKeyDefault )
91+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.accentColor)
92+
93+ val showGaugeColorBand: StateFlow <Boolean > = settingsRepository.settings
94+ .map { it.showGaugeColorBand }
95+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.showGaugeColorBand)
96+
97+ // Orange/red threshold percentages for the gauge color band (both 0-100).
98+ val gaugeOrangePct: StateFlow <Int > = settingsRepository.settings
99+ .map { it.gaugeOrangeThresholdPct }
100+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.gaugeOrangeThresholdPct)
101+
102+ val gaugeRedPct: StateFlow <Int > = settingsRepository.settings
103+ .map { it.gaugeRedThresholdPct }
104+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.gaugeRedThresholdPct)
105+
106+ val currentDisplayMode: StateFlow <String > = settingsRepository.settings
107+ .map { it.currentDisplayMode }
108+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.currentDisplayMode)
109+
110+ val hasFlicConfigured: StateFlow <Boolean > = settingsRepository.settings
111+ .map { it.flic1Address != null || it.flic2Address != null }
112+ .stateIn(viewModelScope, SharingStarted .Eagerly , initialSettings.flic1Address != null || initialSettings.flic2Address != null )
113+
114+ val flicFlashAt: StateFlow <Long > = flicManager.lastActionAt
115+
116+ fun toggleCurrentDisplayMode () {
117+ viewModelScope.launch {
118+ val current = settingsRepository.get()
119+ val next = if (current.currentDisplayMode == " WATTS" ) " AMPS" else " WATTS"
120+ settingsRepository.update(current.copy(currentDisplayMode = next))
121+ }
122+ }
123+
124+ fun startRecording () {
125+ viewModelScope.launch { tripRepository.startRecording() }
126+ }
127+
128+ fun stopRecording () {
129+ viewModelScope.launch { tripRepository.stopRecording() }
130+ }
76131
77132 val modelName: StateFlow <String ?> = wheelRepository.modelName
78133
0 commit comments