From 01139f76e7a53fb194e9e306a130f24961448f2a Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sat, 18 Jan 2025 13:13:19 +0000 Subject: [PATCH] Implemented EclipsedIndicator and MapScroll on select --- .../look4sat/presentation/map/MapScreen.kt | 35 +++--- .../presentation/passes/PassesScreen.kt | 3 +- .../presentation/passes/PassesViewModel.kt | 25 ++--- .../presentation/radar/RadarScreen.kt | 101 +++++++++++++----- .../presentation/settings/SettingsScreen.kt | 24 +++-- .../settings/SettingsViewModel.kt | 59 +++++----- 6 files changed, 147 insertions(+), 100 deletions(-) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt index 5bf39a5a..dd30d36b 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -82,19 +83,20 @@ fun NavGraphBuilder.mapDestination() { composable(Screen.Map.route) { val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory) val uiState = viewModel.uiState.collectAsStateWithLifecycle() - MapScreen(uiState) + val mapView = rememberMapViewWithLifecycle() + MapScreen(uiState, mapView) } } @Composable -private fun MapScreen(uiState: State) { +private fun MapScreen(uiState: State, mapView: MapView) { val onItemClick = { item: OrbitalObject -> uiState.value.sendAction(MapAction.SelectItem(item)) } val selectPrev = { uiState.value.sendAction(MapAction.SelectPrev) } val selectNext = { uiState.value.sendAction(MapAction.SelectNext) } val rotateMod = Modifier.rotate(180f) val timeString = uiState.value.mapData?.aosTime ?: "00:00:00" val isTimeAos = uiState.value.mapData?.isTimeAos ?: true - val osmInfo = "Wikimedia maps | Map data © OpenStreetMap contributors" + val osmInfo = "© OpenStreetMap contributors" Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { TimerRow { CardIcon(onClick = selectPrev, iconId = R.drawable.ic_arrow, modifier = rotateMod) @@ -104,7 +106,12 @@ private fun MapScreen(uiState: State) { NextPassRow(pass = uiState.value.orbitalPass) ElevatedCard(modifier = Modifier.weight(1f)) { Box(contentAlignment = Alignment.BottomCenter) { - MapView(isLightUi = uiState.value.isLightUi) { mapView -> + LaunchedEffect(uiState.value.track) { + val latitude = uiState.value.track?.get(0)?.get(0)?.latitude ?: 0.0 + val longitude = uiState.value.track?.get(0)?.get(0)?.longitude ?: 0.0 + mapView.controller.animateTo(GeoPoint(latitude, longitude)) + } + AndroidView({ mapView }) { mapView -> uiState.value.stationPosition?.let { setStationPosition(it, mapView) } uiState.value.positions?.let { setPositions(it, mapView, onItemClick) } uiState.value.track?.let { setSatelliteTrack(it, mapView) } @@ -207,7 +214,6 @@ private fun getCustomTextIcon(textLabel: String, mapView: MapView): Drawable { } private fun setSatelliteTrack(satTrack: List>, mapView: MapView) { -// val center = GeoPoint(satTrack[0][0].lat, satTrack[0][0].lon) val trackOverlay = FolderOverlay() try { satTrack.forEach { track -> @@ -222,7 +228,6 @@ private fun setSatelliteTrack(satTrack: List>, mapView: MapView) { } catch (exception: Exception) { println(exception) } -// mapView.controller.animateTo(center) } private fun setFootprint(orbitalPos: OrbitalPos, mapView: MapView) { @@ -239,17 +244,7 @@ private fun setFootprint(orbitalPos: OrbitalPos, mapView: MapView) { } @Composable -private fun MapView( - modifier: Modifier = Modifier, - isLightUi: Boolean, - update: ((map: MapView) -> Unit)? = null -) { - val mapView = rememberMapViewWithLifecycle(isLightUi) - AndroidView({ mapView }, modifier) { update?.invoke(it) } -} - -@Composable -private fun rememberMapViewWithLifecycle(isLightTheme: Boolean): MapView { +private fun rememberMapViewWithLifecycle(): MapView { val tileSource = XYTileSource("tiles", 0, 6, 256, ".webp", emptyArray()) val context = LocalContext.current val mapView = remember { MapView(context) }.apply { @@ -263,7 +258,7 @@ private fun rememberMapViewWithLifecycle(isLightTheme: Boolean): MapView { zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER) overlayManager.tilesOverlay.loadingBackgroundColor = Color.TRANSPARENT overlayManager.tilesOverlay.loadingLineColor = Color.TRANSPARENT - overlayManager.tilesOverlay.setColorFilter(getColorFilter(isLightTheme)) + overlayManager.tilesOverlay.setColorFilter(getColorFilter()) setScrollableAreaLimitLatitude(maxLat, minLat, 0) // add overlays: 0 - GSP, 1 - SatTrack, 2 - SatFootprint, 3 - SatIcons overlays.addAll(Array(4) { FolderOverlay() }) @@ -290,13 +285,13 @@ private fun rememberMapViewLifecycleObserver(mapView: MapView) = remember(mapVie } @Composable -private fun getColorFilter(isLightTheme: Boolean): ColorMatrixColorFilter { +private fun getColorFilter(): ColorMatrixColorFilter { val grayScale = ColorMatrix().apply { setSaturation(0f) } val negative = ColorMatrix( floatArrayOf(-1f, 0f, 0f, 0f, 260f, 0f, -1f, 0f, 0f, 260f, 0f, 0f, -1f, 0f, 260f, 0f, 0f, 0f, 1f, 0f) ) negative.preConcat(grayScale) - return if (isLightTheme) ColorMatrixColorFilter(grayScale) else ColorMatrixColorFilter(negative) + return ColorMatrixColorFilter(negative) } private fun getMinZoom(screenHeight: Int): Double { diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt index c566564d..5174d26f 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable @@ -60,7 +61,7 @@ fun NavGraphBuilder.passesDestination(navigateToRadar: (Int, Long) -> Unit) { modelClass = PassesViewModel::class.java, factory = PassesViewModel.Factory ) - val uiState = viewModel.uiState.value + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value PassesScreen(uiState, navigateToRadar) } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt index 73863558..5f5f39d2 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt @@ -17,8 +17,6 @@ */ package com.rtbishop.look4sat.presentation.passes -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope @@ -34,6 +32,9 @@ import com.rtbishop.look4sat.presentation.components.getDefaultPass import kotlinx.coroutines.Job import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -41,7 +42,7 @@ class PassesViewModel( private val satelliteRepo: ISatelliteRepo, private val settingsRepo: ISettingsRepo ) : ViewModel() { - private val _uiState = mutableStateOf( + private val _uiState = MutableStateFlow( PassesState( isPassesDialogShown = false, isRadiosDialogShown = false, @@ -58,7 +59,7 @@ class PassesViewModel( ) ) private var processing: Job? = null - val uiState: State = _uiState + val uiState: StateFlow = _uiState init { viewModelScope.launch { @@ -69,7 +70,7 @@ class PassesViewModel( val timeNow = System.currentTimeMillis() val newPasses = satelliteRepo.processPasses(passes, timeNow) setPassInfo(newPasses, timeNow) - _uiState.value = _uiState.value.copy(isRefreshing = false, itemsList = newPasses) + _uiState.update { it.copy(isRefreshing = false, itemsList = newPasses) } delay(1000) } } @@ -88,15 +89,15 @@ class PassesViewModel( } private fun applyFilter(hoursAhead: Int, minElevation: Double, modes: List) = viewModelScope.launch { - _uiState.value = _uiState.value.copy(isRefreshing = true) + _uiState.update { it.copy(isRefreshing = true) } processing?.cancelAndJoin() settingsRepo.setPassesSettings(PassesSettings(hoursAhead, minElevation, modes)) - _uiState.value = _uiState.value.copy(hours = hoursAhead, elevation = minElevation, modes = modes) + _uiState.update { it.copy(hours = hoursAhead, elevation = minElevation, modes = modes) } satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation, modes) } private fun refreshPasses() = viewModelScope.launch { - _uiState.value = _uiState.value.copy(isRefreshing = true) + _uiState.update { it.copy(isRefreshing = true) } processing?.cancelAndJoin() val (hoursAhead, minElevation, modes) = settingsRepo.passesSettings.value satelliteRepo.calculatePasses(System.currentTimeMillis(), hoursAhead, minElevation, modes) @@ -107,22 +108,22 @@ class PassesViewModel( try { val nextPass = passes.first { it.aosTime.minus(timeNow) > 0 } val time = nextPass.aosTime.minus(timeNow).toTimerString() - _uiState.value = _uiState.value.copy(nextPass = nextPass, nextTime = time, isNextTimeAos = true) + _uiState.update { it.copy(nextPass = nextPass, nextTime = time, isNextTimeAos = true) } } catch (exception: NoSuchElementException) { val lastPass = passes.last() val time = lastPass.losTime.minus(timeNow).toTimerString() - _uiState.value = _uiState.value.copy(nextPass = lastPass, nextTime = time, isNextTimeAos = false) + _uiState.update { it.copy(nextPass = lastPass, nextTime = time, isNextTimeAos = false) } } } private fun toggleFilterDialog() { val currentDialogState = _uiState.value.isPassesDialogShown - _uiState.value = _uiState.value.copy(isPassesDialogShown = currentDialogState.not()) + _uiState.update { it.copy(isPassesDialogShown = currentDialogState.not()) } } private fun toggleRadiosDialog() { val currentDialogState = _uiState.value.isRadiosDialogShown - _uiState.value = _uiState.value.copy(isRadiosDialogShown = currentDialogState.not()) + _uiState.update { it.copy(isRadiosDialogShown = currentDialogState.not()) } } companion object { diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt index 21f6703b..7bb63041 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt @@ -15,10 +15,13 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.ElevatedCard @@ -101,7 +104,8 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) { } Column( verticalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxSize() .padding(horizontal = 6.dp, vertical = 4.dp) ) { Row( @@ -135,34 +139,35 @@ private fun RadarScreen(uiState: RadarState, navigateBack: () -> Unit) { ) } } - if (uiState.orbitalPos.eclipsed) { - val infiniteTransition = rememberInfiniteTransition() - val textAlpha = infiniteTransition.animateFloat( - initialValue = 1.0f, - targetValue = 0.0f, - animationSpec = infiniteRepeatable( - animation = tween( - durationMillis = 500, - delayMillis = 25, - easing = LinearEasing - ), - repeatMode = RepeatMode.Reverse - )) - Text( - text = "Eclipsed!", - fontSize = 18.sp, - fontWeight = FontWeight.Medium, - color = MaterialTheme.colorScheme.primary, - modifier = Modifier - .alpha(textAlpha.value) - .border(2.dp, MaterialTheme.colorScheme.primary, MaterialTheme.shapes.medium) - .padding(16.dp) - ) - } } } ElevatedCard(modifier = Modifier.fillMaxSize()) { - TransmittersList(transmitters = uiState.transmitters) + if (uiState.transmitters.isEmpty()) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Icon( + painter = painterResource(R.drawable.ic_satellite), + contentDescription = null, + modifier = Modifier.size(64.dp) + ) + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "This satellite doesn't have any known transcievers...", + textAlign = TextAlign.Center, + fontSize = 18.sp + ) + } + } else { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + TransmittersList(transmitters = uiState.transmitters) + if (uiState.orbitalPos?.eclipsed == true) { + EclipsedIndicator() + } + } + } } } } @@ -207,6 +212,42 @@ private fun TransmitterItemPreview() { MainTheme { TransmitterItem(transmitter) } } +@Composable +private fun EclipsedIndicator() { + val bgColor = MaterialTheme.colorScheme.primary + val bgShape = MaterialTheme.shapes.medium + val infiniteTransition = rememberInfiniteTransition() + val textAlpha = infiniteTransition.animateFloat( + initialValue = 1.0f, + targetValue = 0.0f, + animationSpec = infiniteRepeatable( + animation = tween( + durationMillis = 1000, + delayMillis = 25, + easing = LinearEasing + ), + repeatMode = RepeatMode.Reverse + ) + ) + Box( + modifier = Modifier + .fillMaxSize() + .alpha(textAlpha.value) + .border(width = 2.dp, color = bgColor, shape = bgShape), + contentAlignment = Alignment.Center + ) { + Text( + text = "Eclipsed!", + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.background, + modifier = Modifier + .background(color = bgColor, shape = bgShape) + .padding(12.dp) + ) + } +} + @Composable private fun TransmitterItem(radio: SatRadio) { Surface(color = MaterialTheme.colorScheme.background) { @@ -224,7 +265,9 @@ private fun TransmitterItem(radio: SatRadio) { Icon( painter = painterResource(id = R.drawable.ic_arrow), tint = Color.Green, - contentDescription = null, modifier = Modifier.rotate(90f).weight(0.15f) + contentDescription = null, modifier = Modifier + .rotate(90f) + .weight(0.15f) ) Text( text = if (radio.isInverted) "INVERTED: ${radio.info} " else "${radio.info} ", @@ -239,7 +282,9 @@ private fun TransmitterItem(radio: SatRadio) { Icon( painter = painterResource(id = R.drawable.ic_arrow), tint = Color.Red, - contentDescription = null, modifier = Modifier.rotate(-90f).weight(0.15f) + contentDescription = null, modifier = Modifier + .rotate(-90f) + .weight(0.15f) ) } FrequencyRow(satRadio = radio) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt index bba64fb6..50c5fcac 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable @@ -43,6 +44,7 @@ import com.rtbishop.look4sat.presentation.Screen import com.rtbishop.look4sat.presentation.components.CardButton import com.rtbishop.look4sat.presentation.components.gotoUrl import com.rtbishop.look4sat.presentation.components.showToast +import org.osmdroid.library.BuildConfig import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -88,7 +90,7 @@ private fun SettingsScreen() { } // Position settings - val positionSettings = viewModel.positionSettings.value + val positionSettings = viewModel.positionSettings.collectAsStateWithLifecycle().value val stationPos = positionSettings.stationPos val setGpsPos = { locationRequest.launch(arrayOf(locationPermCoarse, locationPermFine)) } val setGeoPos = { lat: Double, lon: Double -> viewModel.setGeoPosition(lat, lon) } @@ -106,13 +108,13 @@ private fun SettingsScreen() { } // Data settings - val dataSettings = viewModel.dataSettings.value + val dataSettings = viewModel.dataSettings.collectAsStateWithLifecycle().value val updateFromWeb: () -> Unit = { viewModel.updateFromWeb() } val updateFromFile = { contentRequest.launch("*/*") } val clearAllData: () -> Unit = { viewModel.clearAllData() } // Other settings - val otherSettings = viewModel.otherSettings.value + val otherSettings = viewModel.otherSettings.collectAsStateWithLifecycle().value val toggleUtc = { value: Boolean -> viewModel.toggleUtc(value) } val toggleUpdate = { value: Boolean -> viewModel.toggleUpdate(value) } val toggleSweep = { value: Boolean -> viewModel.toggleSweep(value) } @@ -428,13 +430,15 @@ private fun OtherCard( Text(text = stringResource(id = R.string.other_switch_sensors)) Switch(checked = settings.stateOfSensors, onCheckedChange = { toggleSensor(it) }) } - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - Text(text = stringResource(id = R.string.other_switch_light_theme)) - Switch(checked = settings.stateOfLightTheme, onCheckedChange = { toggleLightTheme(it) }) + if (BuildConfig.DEBUG) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = stringResource(id = R.string.other_switch_light_theme)) + Switch(checked = settings.stateOfLightTheme, onCheckedChange = { toggleLightTheme(it) }) + } } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt index 1d03ee6d..fc8025e9 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt @@ -17,8 +17,6 @@ */ package com.rtbishop.look4sat.presentation.settings -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope @@ -29,6 +27,9 @@ import com.rtbishop.look4sat.R import com.rtbishop.look4sat.domain.model.OtherSettings import com.rtbishop.look4sat.domain.repository.IDatabaseRepo import com.rtbishop.look4sat.domain.repository.ISettingsRepo +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch class SettingsViewModel( @@ -36,89 +37,89 @@ class SettingsViewModel( ) : ViewModel() { private val defaultPosSettings = PositionSettings(false, settingsRepo.stationPosition.value, 0) - private val _positionSettings = mutableStateOf(defaultPosSettings) - val positionSettings: State = _positionSettings + private val _positionSettings = MutableStateFlow(defaultPosSettings) + val positionSettings: StateFlow = _positionSettings private val defaultDataSettings = DataSettings(false, 0, 0, 0L) - private val _dataSettings = mutableStateOf(defaultDataSettings) - val dataSettings: State = _dataSettings + private val _dataSettings = MutableStateFlow(defaultDataSettings) + val dataSettings: StateFlow = _dataSettings - private val _otherSettings = mutableStateOf(settingsRepo.otherSettings.value) - val otherSettings: State = _otherSettings + private val _otherSettings = MutableStateFlow(settingsRepo.otherSettings.value) + val otherSettings: StateFlow = _otherSettings init { viewModelScope.launch { settingsRepo.stationPosition.collect { geoPos -> - _positionSettings.value = - _positionSettings.value.copy(isUpdating = false, stationPos = geoPos) + _positionSettings.update { it.copy(isUpdating = false, stationPos = geoPos) } } } viewModelScope.launch { settingsRepo.databaseState.collect { state -> - _dataSettings.value = _dataSettings.value.copy( - isUpdating = false, - entriesTotal = state.numberOfSatellites, - radiosTotal = state.numberOfRadios, - timestamp = state.updateTimestamp - ) + _dataSettings.update { + it.copy( + isUpdating = false, + entriesTotal = state.numberOfSatellites, + radiosTotal = state.numberOfRadios, + timestamp = state.updateTimestamp + ) + } } } viewModelScope.launch { - settingsRepo.otherSettings.collect { _otherSettings.value = it } + settingsRepo.otherSettings.collect { settings -> _otherSettings.update { settings } } } } fun setGpsPosition() { if (settingsRepo.setStationPositionGps()) { val messageResId = R.string.location_success - _positionSettings.value = - _positionSettings.value.copy(isUpdating = true, messageResId = messageResId) + _positionSettings.update { it.copy(isUpdating = true, messageResId = messageResId) } } else { val errorResId = R.string.location_gps_error - _positionSettings.value = _positionSettings.value.copy(messageResId = errorResId) + _positionSettings.update { it.copy(messageResId = errorResId) } } } fun setGeoPosition(latitude: Double, longitude: Double) { if (settingsRepo.setStationPositionGeo(latitude, longitude, 0.0)) { val messageResId = R.string.location_success - _positionSettings.value = _positionSettings.value.copy(messageResId = messageResId) + _positionSettings.update { it.copy(messageResId = messageResId) } } else { val errorResId = R.string.location_manual_error - _positionSettings.value = _positionSettings.value.copy(messageResId = errorResId) + _positionSettings.update { it.copy(messageResId = errorResId) } } } fun setQthPosition(locator: String) { if (settingsRepo.setStationPositionQth(locator)) { val messageResId = R.string.location_success - _positionSettings.value = _positionSettings.value.copy(messageResId = messageResId) + _positionSettings.update { it.copy(messageResId = messageResId) } } else { val errorResId = R.string.location_qth_error - _positionSettings.value = _positionSettings.value.copy(messageResId = errorResId) + _positionSettings.update { it.copy(messageResId = errorResId) } } } fun dismissPosMessage() { - _positionSettings.value = _positionSettings.value.copy(messageResId = 0) + _positionSettings.update { it.copy(messageResId = 0) } } fun updateFromWeb() = viewModelScope.launch { try { - _dataSettings.value = _dataSettings.value.copy(isUpdating = true) + _dataSettings.update { it.copy(isUpdating = true) } databaseRepo.updateFromRemote() } catch (exception: Exception) { - _dataSettings.value = _dataSettings.value.copy(isUpdating = false) + _dataSettings.update { it.copy(isUpdating = false) } println(exception) } } fun updateFromFile(uri: String) = viewModelScope.launch { try { - _dataSettings.value = _dataSettings.value.copy(isUpdating = true) + _dataSettings.update { it.copy(isUpdating = true) } databaseRepo.updateFromFile(uri) } catch (exception: Exception) { - _dataSettings.value = _dataSettings.value.copy(isUpdating = false) + _dataSettings.update { it.copy(isUpdating = false) } println(exception) } }