Spårning: fixa fasbytet (stale state) + loggvisaren till inställningarna
All checks were successful
release / build-release (push) Successful in 4m12s
All checks were successful
release / build-release (push) Successful in 4m12s
- onLocationChanged skrev tillbaka ett gammalt state-snapshot och nollade SEARCHING→ACTIVE-övergången — klockan stod kvar på 00:00 trots fix (syntes som dubbla 'aktiverad'-rader i loggen). Nu utgår slutskrivningen från aktuellt state. - Teknisk spårningslogg är nu en switch under Inställningar → Felsökning, av som standard. Version 0.8.2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C2GA94E1f3cmrvdQLQhYdg
This commit is contained in:
@@ -29,7 +29,7 @@ android {
|
|||||||
minSdk = 31
|
minSdk = 31
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = ciRunNumber ?: 1
|
versionCode = ciRunNumber ?: 1
|
||||||
versionName = "0.8.1" + (ciRunNumber?.let { "+build.$it" } ?: "-dev")
|
versionName = "0.8.2" + (ciRunNumber?.let { "+build.$it" } ?: "-dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ data class AppSettings(
|
|||||||
val birthYear: Int? = null,
|
val birthYear: Int? = null,
|
||||||
/** null = inte satt, false = man, true = kvinna */
|
/** null = inte satt, false = man, true = kvinna */
|
||||||
val isFemale: Boolean? = null,
|
val isFemale: Boolean? = null,
|
||||||
|
/** Visa teknisk logg på spårskärmen (felsökning) */
|
||||||
|
val trackLogEnabled: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
class SettingsStore(private val context: Context) {
|
class SettingsStore(private val context: Context) {
|
||||||
@@ -58,9 +60,14 @@ class SettingsStore(private val context: Context) {
|
|||||||
heightCm = prefs[KEY_HEIGHT_CM]?.toDoubleOrNull(),
|
heightCm = prefs[KEY_HEIGHT_CM]?.toDoubleOrNull(),
|
||||||
birthYear = prefs[KEY_BIRTH_YEAR],
|
birthYear = prefs[KEY_BIRTH_YEAR],
|
||||||
isFemale = prefs[KEY_IS_FEMALE]?.toBooleanStrictOrNull(),
|
isFemale = prefs[KEY_IS_FEMALE]?.toBooleanStrictOrNull(),
|
||||||
|
trackLogEnabled = prefs[KEY_TRACK_LOG]?.toBooleanStrictOrNull() ?: false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun setTrackLogEnabled(value: Boolean) {
|
||||||
|
context.settingsDataStore.edit { it[KEY_TRACK_LOG] = value.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun setScale(address: String?, name: String?, driver: String?) {
|
suspend fun setScale(address: String?, name: String?, driver: String?) {
|
||||||
context.settingsDataStore.edit { prefs ->
|
context.settingsDataStore.edit { prefs ->
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
@@ -132,5 +139,6 @@ class SettingsStore(private val context: Context) {
|
|||||||
private val KEY_HEIGHT_CM = stringPreferencesKey("height_cm")
|
private val KEY_HEIGHT_CM = stringPreferencesKey("height_cm")
|
||||||
private val KEY_BIRTH_YEAR = intPreferencesKey("birth_year")
|
private val KEY_BIRTH_YEAR = intPreferencesKey("birth_year")
|
||||||
private val KEY_IS_FEMALE = stringPreferencesKey("is_female")
|
private val KEY_IS_FEMALE = stringPreferencesKey("is_female")
|
||||||
|
private val KEY_TRACK_LOG = stringPreferencesKey("track_log_enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,10 +251,13 @@ class TrackingService : Service(), LocationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lastLocation = location
|
lastLocation = location
|
||||||
_state.value = s.copy(
|
// Utgå från AKTUELLT state (inte snapshotet från funktionens start) —
|
||||||
|
// annars skrivs t.ex. fasbytet till ACTIVE över och klockan står still.
|
||||||
|
val cur = _state.value
|
||||||
|
_state.value = cur.copy(
|
||||||
distanceMeters = distance,
|
distanceMeters = distance,
|
||||||
elevationGainMeters = elevGain,
|
elevationGainMeters = elevGain,
|
||||||
points = s.points + (location.latitude to location.longitude),
|
points = cur.points + (location.latitude to location.longitude),
|
||||||
currentSpeedKmh = if (location.hasSpeed()) location.speed * 3.6 else null,
|
currentSpeedKmh = if (location.hasSpeed()) location.speed * 3.6 else null,
|
||||||
gpsFix = true,
|
gpsFix = true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ class SettingsViewModel(
|
|||||||
fun addCustomBar(value: Double) = viewModelScope.launch { store.addCustomBarWeight(value) }
|
fun addCustomBar(value: Double) = viewModelScope.launch { store.addCustomBarWeight(value) }
|
||||||
fun removeCustomBar(value: Double) = viewModelScope.launch { store.removeCustomBarWeight(value) }
|
fun removeCustomBar(value: Double) = viewModelScope.launch { store.removeCustomBarWeight(value) }
|
||||||
|
|
||||||
|
fun setTrackLog(value: Boolean) = viewModelScope.launch { store.setTrackLogEnabled(value) }
|
||||||
|
|
||||||
fun setAutoRelogin(value: Boolean) = viewModelScope.launch {
|
fun setAutoRelogin(value: Boolean) = viewModelScope.launch {
|
||||||
store.setAutoRelogin(value)
|
store.setAutoRelogin(value)
|
||||||
// Stängs funktionen av slängs de sparade uppgifterna direkt.
|
// Stängs funktionen av slängs de sparade uppgifterna direkt.
|
||||||
@@ -145,6 +147,28 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsCard(title = "Felsökning") {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 6.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text("Teknisk spårningslogg", style = MaterialTheme.typography.bodyLarge)
|
||||||
|
Text(
|
||||||
|
"Visar GPS-loggen på aktivitetsskärmen (för felsökning).",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
androidx.compose.material3.Switch(
|
||||||
|
checked = settings.trackLogEnabled,
|
||||||
|
onCheckedChange = viewModel::setTrackLog,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsCard(title = "Vilotimerns utseende") {
|
SettingsCard(title = "Vilotimerns utseende") {
|
||||||
RadioRow(
|
RadioRow(
|
||||||
label = "Helskärm",
|
label = "Helskärm",
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import eu.brassepc.fitnessdroid.ui.appContainer
|
|||||||
import eu.brassepc.fitnessdroid.ui.common.compact
|
import eu.brassepc.fitnessdroid.ui.common.compact
|
||||||
import eu.brassepc.fitnessdroid.ui.common.openAppSettings
|
import eu.brassepc.fitnessdroid.ui.common.openAppSettings
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.osmdroid.config.Configuration
|
import org.osmdroid.config.Configuration
|
||||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||||
@@ -79,8 +80,11 @@ import java.time.Instant
|
|||||||
class TrackViewModel(
|
class TrackViewModel(
|
||||||
private val gymApi: GymApi,
|
private val gymApi: GymApi,
|
||||||
private val repo: GymRepository,
|
private val repo: GymRepository,
|
||||||
|
settingsStore: eu.brassepc.fitnessdroid.data.SettingsStore,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val tracking = TrackingService.state
|
val tracking = TrackingService.state
|
||||||
|
/** Teknisk logg visas bara om den slagits på i inställningarna. */
|
||||||
|
val logEnabled = settingsStore.settings.map { it.trackLogEnabled }
|
||||||
val message = MutableStateFlow<String?>(null)
|
val message = MutableStateFlow<String?>(null)
|
||||||
val saved = MutableStateFlow(false)
|
val saved = MutableStateFlow(false)
|
||||||
/** Sammanfattningen som visas efter stopp (null = spårning pågår/ej startad) */
|
/** Sammanfattningen som visas efter stopp (null = spårning pågår/ej startad) */
|
||||||
@@ -168,7 +172,7 @@ class TrackViewModel(
|
|||||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
val c = appContainer()
|
val c = appContainer()
|
||||||
TrackViewModel(c.gymApi, c.gymRepository)
|
TrackViewModel(c.gymApi, c.gymRepository, c.settingsStore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,6 +190,7 @@ fun TrackScreen(
|
|||||||
val summary by viewModel.summary.collectAsStateWithLifecycle()
|
val summary by viewModel.summary.collectAsStateWithLifecycle()
|
||||||
val saved by viewModel.saved.collectAsStateWithLifecycle()
|
val saved by viewModel.saved.collectAsStateWithLifecycle()
|
||||||
val message by viewModel.message.collectAsStateWithLifecycle()
|
val message by viewModel.message.collectAsStateWithLifecycle()
|
||||||
|
val logEnabled by viewModel.logEnabled.collectAsStateWithLifecycle(false)
|
||||||
|
|
||||||
val pendingType by viewModel.pendingType.collectAsStateWithLifecycle()
|
val pendingType by viewModel.pendingType.collectAsStateWithLifecycle()
|
||||||
val typeLoadError by viewModel.typeLoadError.collectAsStateWithLifecycle()
|
val typeLoadError by viewModel.typeLoadError.collectAsStateWithLifecycle()
|
||||||
@@ -276,6 +281,7 @@ fun TrackScreen(
|
|||||||
message = message,
|
message = message,
|
||||||
onSave = { rpe, overrideId -> viewModel.save(rpe, overrideId) },
|
onSave = { rpe, overrideId -> viewModel.save(rpe, overrideId) },
|
||||||
onDone = onBack,
|
onDone = onBack,
|
||||||
|
showLog = logEnabled,
|
||||||
)
|
)
|
||||||
permissionDenied -> Column(
|
permissionDenied -> Column(
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
@@ -323,6 +329,7 @@ fun TrackScreen(
|
|||||||
tracking = tracking,
|
tracking = tracking,
|
||||||
onStop = { viewModel.stop(context) },
|
onStop = { viewModel.stop(context) },
|
||||||
onStartAnyway = { TrackingService.startNow(context) },
|
onStartAnyway = { TrackingService.startNow(context) },
|
||||||
|
showLog = logEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,6 +341,7 @@ private fun LiveContent(
|
|||||||
tracking: TrackingState,
|
tracking: TrackingState,
|
||||||
onStop: () -> Unit,
|
onStop: () -> Unit,
|
||||||
onStartAnyway: () -> Unit,
|
onStartAnyway: () -> Unit,
|
||||||
|
showLog: Boolean,
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
if (tracking.isDistanceBased) {
|
if (tracking.isDistanceBased) {
|
||||||
@@ -424,7 +432,7 @@ private fun LiveContent(
|
|||||||
Icon(Icons.Default.Stop, contentDescription = null)
|
Icon(Icons.Default.Stop, contentDescription = null)
|
||||||
Text("Avsluta aktiviteten", modifier = Modifier.padding(start = 6.dp))
|
Text("Avsluta aktiviteten", modifier = Modifier.padding(start = 6.dp))
|
||||||
}
|
}
|
||||||
TechLogSection()
|
if (showLog) TechLogSection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,6 +569,7 @@ private fun SummaryContent(
|
|||||||
message: String?,
|
message: String?,
|
||||||
onSave: (Double?, Int?) -> Unit,
|
onSave: (Double?, Int?) -> Unit,
|
||||||
onDone: () -> Unit,
|
onDone: () -> Unit,
|
||||||
|
showLog: Boolean,
|
||||||
) {
|
) {
|
||||||
var rpe by remember { mutableStateOf(5f) }
|
var rpe by remember { mutableStateOf(5f) }
|
||||||
|
|
||||||
@@ -635,7 +644,7 @@ private fun SummaryContent(
|
|||||||
|
|
||||||
message?.let { Text(it, color = MaterialTheme.colorScheme.error) }
|
message?.let { Text(it, color = MaterialTheme.colorScheme.error) }
|
||||||
|
|
||||||
TechLogSection()
|
if (showLog) TechLogSection()
|
||||||
|
|
||||||
if (saved) {
|
if (saved) {
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
Reference in New Issue
Block a user