Further classes restructuring and code cleanup

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:38 +00:00
1 parent f6fcd21404
commit a807cfd8f6
20 files changed
+292 -329

No files matched your search

-1
View File
@@ -65,7 +65,6 @@ dependencies {
implementation "androidx.compose.compiler:compiler:$compose_compiler_version"
implementation "androidx.compose.material3:material3:$material3_version"
implementation "androidx.compose.runtime:runtime:$compose_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
implementation "androidx.navigation:navigation-compose:$navigation_version"
@@ -18,6 +18,7 @@
package com.rtbishop.look4sat
import android.app.Application
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@@ -29,13 +30,13 @@ class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
container = MainContainer(this)
checkAutoUpdate()
container.mainScope.launch { checkAutoUpdate() }
}
private fun checkAutoUpdate() {
private suspend fun checkAutoUpdate() {
val settingsRepo = container.settingsRepo
if (settingsRepo.isUpdateEnabled()) {
val timeDelta = System.currentTimeMillis() - settingsRepo.getLastUpdateTime()
if (settingsRepo.otherSettings.value.updateState) {
val timeDelta = System.currentTimeMillis() - settingsRepo.databaseState.value.timestamp
if (timeDelta > 172800000) { // 48 hours in ms
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
println("Started periodic data update on ${sdf.format(Date())}")
@@ -23,6 +23,7 @@ import com.rtbishop.look4sat.framework.data.DataSource
import com.rtbishop.look4sat.framework.data.LocalStorage
import com.rtbishop.look4sat.framework.data.MIGRATION_1_2
import com.rtbishop.look4sat.framework.data.MainDatabase
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -33,10 +34,13 @@ class MainContainer(private val context: Context) {
private val database = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb")
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
private val localStorage = LocalStorage(database.storageDao())
private val mainHandler = CoroutineExceptionHandler { _, error -> println("Look4Sat: $error") }
val mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + mainHandler)
val settingsRepo = provideSettingsRepo()
val databaseRepo = provideDatabaseRepo(settingsRepo)
val sensorsRepo = provideSensorRepo()
val databaseRepo = provideDatabaseRepo()
val satelliteRepo = provideSatelliteRepo()
val selectionRepo = provideSelectionRepo()
val sensorsRepo = provideSensorsRepo()
fun provideBluetoothReporter(): BluetoothReporter {
val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
@@ -47,27 +51,26 @@ class MainContainer(private val context: Context) {
return NetworkReporter(CoroutineScope(Dispatchers.IO))
}
private fun provideDatabaseRepo(settingsRepo: ISettingsRepo): IDatabaseRepo {
private fun provideDatabaseRepo(): IDatabaseRepo {
val dataParser = DataParser(Dispatchers.Default)
val dataSource = DataSource(context.contentResolver, Dispatchers.IO)
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
return DatabaseRepo(dataParser, localStorage, dataSource, scope, settingsRepo)
}
fun provideSelectionRepo(): ISelectionRepo {
return SelectionRepo(Dispatchers.Default, localStorage, settingsRepo)
}
private fun provideSensorRepo(): ISensorsRepo {
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
return SensorsRepo(sensorManager, sensor)
return DatabaseRepo(Dispatchers.Default, dataParser, dataSource, localStorage, settingsRepo)
}
private fun provideSatelliteRepo(): ISatelliteRepo {
return SatelliteRepo(Dispatchers.Default, localStorage)
}
private fun provideSelectionRepo(): ISelectionRepo {
return SelectionRepo(Dispatchers.Default, localStorage, settingsRepo)
}
private fun provideSensorsRepo(): ISensorsRepo {
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
return SensorsRepo(sensorManager, sensor)
}
private fun provideSettingsRepo(): ISettingsRepo {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
@@ -24,7 +24,9 @@ import androidx.core.content.edit
import androidx.core.location.LocationManagerCompat
import androidx.core.os.CancellationSignal
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.DatabaseState
import com.rtbishop.look4sat.model.GeoPos
import com.rtbishop.look4sat.model.OtherSettings
import com.rtbishop.look4sat.utility.QthConverter
import com.rtbishop.look4sat.utility.round
import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,11 +38,13 @@ class SettingsRepo(
) : ISettingsRepo {
private val keyModes = "satModes"
private val keyCompass = "compass"
private val keyRadarSweep = "radarSweep"
private val keyTimeUTC = "timeUTC"
private val keyLastUpdateTime = "lastUpdateTime"
private val keyAutoUpdateEnabled = "autoUpdateEnabled"
private val keyUtcState = "utcState"
private val keyUpdateState = "updateState"
private val keySweepState = "sweepState"
private val keySensorState = "sensorState"
private val keyDataEntries = "dataEntries"
private val keyDataRadios = "dataRadios"
private val keyDataTimestamp = "dataTimestamp"
private val keyHoursAhead = "hoursAhead"
private val keyMinElevation = "minElevation"
private val keyRotator = "isRotatorEnabled"
@@ -164,44 +168,52 @@ class SettingsRepo(
preferences.edit { putDouble(keyMinElevation, minElevation) }
}
override fun isUtcEnabled(): Boolean {
return preferences.getBoolean(keyTimeUTC, false)
private val _databaseState = MutableStateFlow(loadDatabaseState())
override val databaseState: StateFlow<DatabaseState> = _databaseState
override fun saveDatabaseState(state: DatabaseState) = preferences.edit {
putInt(keyDataEntries, state.entriesTotal)
putInt(keyDataRadios, state.radiosTotal)
putLong(keyDataTimestamp, state.timestamp)
_databaseState.value = state
}
override fun setUtcState(value: Boolean) {
preferences.edit { putBoolean(keyTimeUTC, value) }
private fun loadDatabaseState(): DatabaseState {
val entriesTotal = preferences.getInt(keyDataEntries, 0)
val radiosTotal = preferences.getInt(keyDataRadios, 0)
val timestamp = preferences.getLong(keyDataTimestamp, 0L)
return DatabaseState(entriesTotal, radiosTotal, timestamp)
}
override fun getLastUpdateTime(): Long {
return preferences.getLong(keyLastUpdateTime, 0L)
private val _otherSettings = MutableStateFlow(loadOtherSettings())
override val otherSettings: StateFlow<OtherSettings> = _otherSettings
override fun toggleUtc(value: Boolean) {
preferences.edit { putBoolean(keyUtcState, value) }
_otherSettings.value = otherSettings.value.copy(utcState = value)
}
override fun setLastUpdateTime(updateTime: Long) {
preferences.edit { putLong(keyLastUpdateTime, updateTime) }
override fun toggleUpdate(value: Boolean) {
preferences.edit { putBoolean(keyUpdateState, value) }
_otherSettings.value = otherSettings.value.copy(updateState = value)
}
override fun isUpdateEnabled(): Boolean {
return preferences.getBoolean(keyAutoUpdateEnabled, true)
override fun toggleSweep(value: Boolean) {
preferences.edit { putBoolean(keySweepState, value) }
_otherSettings.value = otherSettings.value.copy(sweepState = value)
}
override fun setUpdateState(value: Boolean) {
preferences.edit { putBoolean(keyAutoUpdateEnabled, value) }
override fun toggleSensor(value: Boolean) {
preferences.edit { putBoolean(keySensorState, value) }
_otherSettings.value = otherSettings.value.copy(sensorState = value)
}
override fun isSensorEnabled(): Boolean {
return preferences.getBoolean(keyCompass, true)
}
override fun setSensorState(value: Boolean) {
preferences.edit { putBoolean(keyCompass, value) }
}
override fun isSweepEnabled(): Boolean {
return preferences.getBoolean(keyRadarSweep, true)
}
override fun setSweepState(value: Boolean) {
preferences.edit { putBoolean(keyRadarSweep, value) }
private fun loadOtherSettings(): OtherSettings {
val utcState = preferences.getBoolean(keyUtcState, false)
val updateState = preferences.getBoolean(keyUpdateState, true)
val sweepState = preferences.getBoolean(keySweepState, true)
val sensorState = preferences.getBoolean(keySensorState, true)
return OtherSettings(utcState, updateState, sweepState, sensorState)
}
override fun saveModesSelection(modes: List<String>) {
@@ -31,7 +31,7 @@ class LocalStorage(private val storageDao: StorageDao) : ILocalStorage {
//region # Entries region
override fun getEntriesTotal() = storageDao.getEntriesTotal()
override suspend fun getEntriesTotal() = storageDao.getEntriesTotal()
override suspend fun getEntriesList(): List<DomainItem> {
return storageDao.getEntriesList().toDomainItems()
@@ -64,7 +64,7 @@ class LocalStorage(private val storageDao: StorageDao) : ILocalStorage {
//region # Radios region
override fun getRadiosTotal() = storageDao.getRadiosTotal()
override suspend fun getRadiosTotal() = storageDao.getRadiosTotal()
override suspend fun getRadiosWithId(id: Int): List<SatRadio> {
return storageDao.getRadiosWithId(id).toDomainRadios()
@@ -24,7 +24,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.SatRadio
@Database(entities = [SatEntry::class, SatRadio::class], version = 2)
@Database(entities = [SatEntry::class, SatRadio::class], version = 2, exportSchema = false)
abstract class MainDatabase : RoomDatabase() {
abstract fun storageDao(): StorageDao
@@ -21,13 +21,12 @@ import androidx.room.*
import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.SatItem
import com.rtbishop.look4sat.framework.model.SatRadio
import kotlinx.coroutines.flow.Flow
@Dao
interface StorageDao {
@Query("SELECT COUNT(*) FROM entries")
fun getEntriesTotal(): Flow<Int>
suspend fun getEntriesTotal(): Int
//@Transaction
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
@@ -44,7 +43,7 @@ interface StorageDao {
suspend fun deleteEntries()
@Query("SELECT COUNT(*) FROM radios")
fun getRadiosTotal(): Flow<Int>
suspend fun getRadiosTotal(): Int
@Transaction
@Query("SELECT * FROM radios WHERE catnum = :id AND isAlive = 1")
@@ -69,7 +69,7 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel()
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer {
val container = (this[applicationKey] as MainApplication).container
EntriesViewModel(container.provideSelectionRepo())
EntriesViewModel(container.selectionRepo)
}
}
}
@@ -89,7 +89,7 @@ class PassesViewModel(
}
}
fun shouldUseUTC() = settingsRepo.isUtcEnabled()
fun shouldUseUTC() = settingsRepo.otherSettings.value.utcState
fun calculatePasses(
hoursAhead: Int = 1,
@@ -61,7 +61,7 @@ class RadarViewModel(
val orientation = mutableStateOf(sensorsRepo.orientation.value)
init {
if (settingsRepo.isSensorEnabled()) {
if (settingsRepo.otherSettings.value.sensorState) {
viewModelScope.launch {
sensorsRepo.enableSensor()
sensorsRepo.orientation.collect { data ->
@@ -100,9 +100,9 @@ class RadarViewModel(
}
}
fun getUseCompass(): Boolean = settingsRepo.isSensorEnabled()
fun getUseCompass(): Boolean = settingsRepo.otherSettings.value.sensorState
fun getShowSweep(): Boolean = settingsRepo.isSweepEnabled()
fun getShowSweep(): Boolean = settingsRepo.otherSettings.value.sweepState
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
val latitude = geoPos.latitude.toFloat()
@@ -38,6 +38,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.model.GeoPos
import com.rtbishop.look4sat.model.OtherSettings
import com.rtbishop.look4sat.presentation.CardButton
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.dialogs.LocatorDialog
@@ -76,43 +77,55 @@ fun SettingsScreen() {
val locationPermFine = Manifest.permission.ACCESS_FINE_LOCATION
val locationRequest = rememberLauncherForActivityResult(locationContract) { permissions ->
when {
permissions[locationPermFine] == true -> viewModel.locationSettings.value.setGpsLoc()
permissions[locationPermCoarse] == true -> viewModel.locationSettings.value.setGpsLoc()
permissions[locationPermFine] == true -> viewModel.setGpsPosition()
permissions[locationPermCoarse] == true -> viewModel.setGpsPosition()
else -> showToast(context, locationError)
}
}
val contentContract = ActivityResultContracts.GetContent()
val contentRequest = rememberLauncherForActivityResult(contentContract) { uri ->
uri?.let { viewModel.dataSettings.value.updateFromFile(uri.toString()) }
uri?.let { viewModel.updateFromFile(uri.toString()) }
}
// Location settings
val locSettings = viewModel.locationSettings.value
val setGpsLoc = { locationRequest.launch(arrayOf(locationPermCoarse, locationPermFine)) }
val showPosDialog = rememberSaveable { mutableStateOf(false) }
val togglePosDialog = { showPosDialog.value = showPosDialog.value.not() }
if (showPosDialog.value) {
PositionDialog(
locSettings.stationPos.latitude,
locSettings.stationPos.longitude,
togglePosDialog,
locSettings.setManualLoc
)
// Position settings
val positionSettings = viewModel.positionSettings.value
val stationPos = positionSettings.stationPos
val setGpsPos = { locationRequest.launch(arrayOf(locationPermCoarse, locationPermFine)) }
val setGeoPos = { lat: Double, lon: Double -> viewModel.setGeoPosition(lat, lon) }
val setQthPos = { locator: String -> viewModel.setQthPosition(locator) }
val dismissPos = { viewModel.dismissPosMessage() }
val posDialogState = rememberSaveable { mutableStateOf(false) }
val showPosDialog = { posDialogState.value = posDialogState.value.not() }
if (posDialogState.value) {
PositionDialog(stationPos.latitude, stationPos.longitude, showPosDialog, setGeoPos)
}
val showLocDialog = rememberSaveable { mutableStateOf(false) }
val toggleLocDialog = { showLocDialog.value = showLocDialog.value.not() }
if (showLocDialog.value) {
LocatorDialog(locSettings.stationPos.qthLocator, toggleLocDialog, locSettings.setQthLoc)
val locDialogState = rememberSaveable { mutableStateOf(false) }
val showLocDialog = { locDialogState.value = locDialogState.value.not() }
if (locDialogState.value) {
LocatorDialog(positionSettings.stationPos.qthLocator, showLocDialog, setQthPos)
}
// Data settings
val dataSettings = viewModel.dataSettings.value
val updateFromWeb: () -> Unit = { viewModel.updateFromWeb() }
val updateFromFile = { contentRequest.launch("*/*") }
val clearAllData: () -> Unit = { viewModel.clearAllData() }
// Other settings
val otherSettings = viewModel.otherSettings.value
val toggleUtc = { value: Boolean -> viewModel.toggleUtc(value) }
val toggleUpdate = { value: Boolean -> viewModel.toggleUpdate(value) }
val toggleSweep = { value: Boolean -> viewModel.toggleSweep(value) }
val toggleSensor = { value: Boolean -> viewModel.toggleSensor(value) }
// Screen setup
LazyColumn(
modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)
) {
item { CardAbout(BuildConfig.VERSION_NAME) }
item { LocationCard(locSettings, setGpsLoc, togglePosDialog, toggleLocDialog) }
item { DataCard(viewModel.dataSettings.value) { contentRequest.launch("*/*") } }
item { OtherCard(viewModel.otherSettings.value) }
item { LocationCard(positionSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos) }
item { DataCard(dataSettings, updateFromWeb, updateFromFile, clearAllData) }
item { OtherCard(otherSettings, toggleUtc, toggleUpdate, toggleSweep, toggleSensor) }
item { CardCredits() }
}
}
@@ -181,16 +194,17 @@ private fun CardAbout(version: String, modifier: Modifier = Modifier) {
@Composable
private fun LocationCardPreview() = MainTheme {
val stationPos = GeoPos(0.0, 0.0, 0.0, "IO91vl", 0L)
val settings = LocationSettings(true, stationPos, 0, {}, { _, _ -> }, {}, {})
LocationCard(settings = settings, setGpsLoc = {}, togglePosDialog = {}) {}
val settings = PositionSettings(true, stationPos, 0)
LocationCard(settings = settings, setGpsPos = {}, showPosDialog = {}, {}) {}
}
@Composable
private fun LocationCard(
settings: LocationSettings,
setGpsLoc: () -> Unit,
togglePosDialog: () -> Unit,
toggleLocDialog: () -> Unit
settings: PositionSettings,
setGpsPos: () -> Unit,
showPosDialog: () -> Unit,
showLocDialog: () -> Unit,
dismissPosMessage: () -> Unit
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(
@@ -227,19 +241,19 @@ private fun LocationCard(
Spacer(modifier = Modifier.height(1.dp))
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
CardButton(
onClick = setGpsLoc,
onClick = setGpsPos,
text = stringResource(id = R.string.btn_gps),
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(6.dp))
CardButton(
onClick = togglePosDialog,
onClick = showPosDialog,
text = stringResource(id = R.string.btn_manual),
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(6.dp))
CardButton(
onClick = toggleLocDialog,
onClick = showLocDialog,
text = stringResource(id = R.string.btn_qth),
modifier = Modifier.weight(1f)
)
@@ -251,7 +265,7 @@ private fun LocationCard(
val errorString = stringResource(id = settings.messageResId)
LaunchedEffect(key1 = settings.messageResId) {
showToast(context, errorString)
settings.dismissMessage()
dismissPosMessage()
}
}
}
@@ -259,12 +273,17 @@ private fun LocationCard(
@Preview(showBackground = true)
@Composable
private fun DataCardPreview() = MainTheme {
val settings = DataSettings(true, 0L, 5000, 2500, {}, {}, {})
DataCard(settings = settings) {}
val settings = DataSettings(true, 5000, 2500, 0L)
DataCard(settings = settings, {}, {}) {}
}
@Composable
private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) {
private fun DataCard(
settings: DataSettings,
updateFromWeb: () -> Unit,
updateFromFile: () -> Unit,
clearAllData: () -> Unit
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@@ -278,29 +297,29 @@ private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) {
UpdateIndicator(isUpdating = settings.isUpdating, Modifier.weight(1f))
}
Spacer(modifier = Modifier.height(2.dp))
Text(text = setUpdateTime(updateTime = settings.lastUpdated))
Text(text = setUpdateTime(updateTime = settings.timestamp))
Spacer(modifier = Modifier.height(2.dp))
Row(horizontalArrangement = Arrangement.SpaceBetween) {
Text(text = "Satellites: ${settings.satsTotal}")
Text(text = "Satellites: ${settings.entriesTotal}")
Spacer(modifier = Modifier.weight(1f))
Text(text = "Transceivers: ${settings.radiosTotal}")
}
Spacer(modifier = Modifier.height(1.dp))
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
CardButton(
onClick = settings.updateFromWeb,
onClick = { updateFromWeb() },
text = stringResource(id = R.string.btn_web),
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(6.dp))
CardButton(
onClick = updateFromFile,
onClick = { updateFromFile() },
text = stringResource(id = R.string.btn_file),
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(6.dp))
CardButton(
onClick = settings.clearAllData,
onClick = { clearAllData() },
text = stringResource(id = R.string.btn_clear),
modifier = Modifier.weight(1f)
)
@@ -357,14 +376,19 @@ private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) {
@Preview(showBackground = true)
@Composable
private fun OtherCardPreview() = MainTheme {
val settings = OtherSettings(
getUtc = false, getUpdate = true, getSweep = true, getSensor = true,
setUtc = {}, setUpdate = {}, setSweep = {}, setSensor = {})
OtherCard(settings = settings)
val values =
OtherSettings(utcState = false, updateState = true, sweepState = true, sensorState = true)
OtherCard(settings = values, {}, {}, {}, {})
}
@Composable
private fun OtherCard(settings: OtherSettings) {
private fun OtherCard(
settings: OtherSettings,
toggleUtc: (Boolean) -> Unit,
toggleUpdate: (Boolean) -> Unit,
toggleSweep: (Boolean) -> Unit,
toggleSensor: (Boolean) -> Unit
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)) {
Row(
@@ -373,7 +397,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(id = R.string.other_switch_utc))
Switch(checked = settings.getUtc, onCheckedChange = { settings.setUtc(it) })
Switch(checked = settings.utcState, onCheckedChange = { toggleUtc(it) })
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
@@ -381,7 +405,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(id = R.string.other_switch_update))
Switch(checked = settings.getUpdate, onCheckedChange = { settings.setUpdate(it) })
Switch(checked = settings.updateState, onCheckedChange = { toggleUpdate(it) })
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
@@ -389,7 +413,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(id = R.string.other_switch_sweep))
Switch(checked = settings.getSweep, onCheckedChange = { settings.setSweep(it) })
Switch(checked = settings.sweepState, onCheckedChange = { toggleSweep(it) })
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
@@ -397,7 +421,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(id = R.string.other_switch_sensors))
Switch(checked = settings.getSensor, onCheckedChange = { settings.setSensor(it) })
Switch(checked = settings.sensorState, onCheckedChange = { toggleSensor(it) })
}
}
}
@@ -2,33 +2,8 @@ package com.rtbishop.look4sat.presentation.settings
import com.rtbishop.look4sat.model.GeoPos
data class LocationSettings(
val isUpdating: Boolean,
val stationPos: GeoPos,
val messageResId: Int,
val setGpsLoc: () -> Unit,
val setManualLoc: (Double, Double) -> Unit,
val setQthLoc: (String) -> Unit,
val dismissMessage: () -> Unit
)
data class PositionSettings(val isUpdating: Boolean, val stationPos: GeoPos, val messageResId: Int)
data class DataSettings(
val isUpdating: Boolean,
val lastUpdated: Long,
val satsTotal: Int,
val radiosTotal: Int,
val updateFromWeb: () -> Unit,
val updateFromFile: (String) -> Unit,
val clearAllData: () -> Unit
)
data class OtherSettings(
val getUtc: Boolean,
val getUpdate: Boolean,
val getSweep: Boolean,
val getSensor: Boolean,
val setUtc: (Boolean) -> Unit,
val setUpdate: (Boolean) -> Unit,
val setSweep: (Boolean) -> Unit,
val setSensor: (Boolean) -> Unit
val isUpdating: Boolean, val entriesTotal: Int, val radiosTotal: Int, val timestamp: Long
)
@@ -17,6 +17,7 @@
*/
package com.rtbishop.look4sat.presentation.settings
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@@ -27,121 +28,107 @@ import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.IDatabaseRepo
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.DataState
import kotlinx.coroutines.flow.combine
import com.rtbishop.look4sat.model.OtherSettings
import kotlinx.coroutines.launch
class SettingsViewModel(
private val databaseRepo: IDatabaseRepo, private val settingsRepo: ISettingsRepo
) : ViewModel() {
private val defaultLocationSettings = LocationSettings(false,
settingsRepo.stationPosition.value,
0,
{ setGpsPosition() },
{ latitude, longitude -> setGeoPosition(latitude, longitude) },
{ locator -> setQthPosition(locator) },
{ dismissLocationMessage() })
val locationSettings = mutableStateOf(defaultLocationSettings)
private val defaultPosSettings = PositionSettings(false, settingsRepo.stationPosition.value, 0)
private val _positionSettings = mutableStateOf(defaultPosSettings)
val positionSettings: State<PositionSettings> = _positionSettings
private val defaultDataSettings = DataSettings(false,
settingsRepo.getLastUpdateTime(),
0,
0,
{ databaseRepo.updateFromWeb() },
{ databaseRepo.updateFromFile(it) },
{ databaseRepo.clearAllData() })
val dataSettings = mutableStateOf(defaultDataSettings)
private val defaultDataSettings = DataSettings(false, 0, 0, 0L)
private val _dataSettings = mutableStateOf(defaultDataSettings)
val dataSettings: State<DataSettings> = _dataSettings
private val defaultOtherSettings = OtherSettings(settingsRepo.isUtcEnabled(),
settingsRepo.isUpdateEnabled(),
settingsRepo.isSweepEnabled(),
settingsRepo.isSensorEnabled(),
{ setUtc(it) },
{ setUpdate(it) },
{ setSweep(it) },
{ setSensor(it) })
val otherSettings = mutableStateOf(defaultOtherSettings)
private val _otherSettings = mutableStateOf(settingsRepo.otherSettings.value)
val otherSettings: State<OtherSettings> = _otherSettings
init {
viewModelScope.launch {
settingsRepo.stationPosition.collect { stationPos ->
locationSettings.value = locationSettings.value.copy(
isUpdating = false, stationPos = stationPos
settingsRepo.stationPosition.collect { geoPos ->
_positionSettings.value =
_positionSettings.value.copy(isUpdating = false, stationPos = geoPos)
}
}
viewModelScope.launch {
settingsRepo.databaseState.collect { state ->
_dataSettings.value = _dataSettings.value.copy(
isUpdating = false,
entriesTotal = state.entriesTotal,
radiosTotal = state.radiosTotal,
timestamp = state.timestamp
)
}
}
viewModelScope.launch {
databaseRepo.updateState.collect {
val isUpdating = it is DataState.Loading
dataSettings.value = dataSettings.value.copy(
isUpdating = isUpdating, lastUpdated = settingsRepo.getLastUpdateTime()
)
}
}
viewModelScope.launch {
combine(
databaseRepo.getEntriesTotal(), databaseRepo.getRadiosTotal()
) { sats, radios ->
dataSettings.value = dataSettings.value.copy(satsTotal = sats, radiosTotal = radios)
}.collect {}
settingsRepo.otherSettings.collect { _otherSettings.value = it }
}
}
private fun setGpsPosition() {
fun setGpsPosition() {
if (settingsRepo.setGpsPosition()) {
val messageResId = R.string.location_success
locationSettings.value =
locationSettings.value.copy(isUpdating = true, messageResId = messageResId)
_positionSettings.value =
_positionSettings.value.copy(isUpdating = true, messageResId = messageResId)
} else {
val errorResId = R.string.location_gps_error
locationSettings.value = locationSettings.value.copy(messageResId = errorResId)
_positionSettings.value = _positionSettings.value.copy(messageResId = errorResId)
}
}
private fun setGeoPosition(latitude: Double, longitude: Double) {
fun setGeoPosition(latitude: Double, longitude: Double) {
if (settingsRepo.setGeoPosition(latitude, longitude)) {
val messageResId = R.string.location_success
locationSettings.value = locationSettings.value.copy(messageResId = messageResId)
_positionSettings.value = _positionSettings.value.copy(messageResId = messageResId)
} else {
val errorResId = R.string.location_manual_error
locationSettings.value = locationSettings.value.copy(messageResId = errorResId)
_positionSettings.value = _positionSettings.value.copy(messageResId = errorResId)
}
}
private fun setQthPosition(locator: String) {
fun setQthPosition(locator: String) {
if (settingsRepo.setQthPosition(locator)) {
val messageResId = R.string.location_success
locationSettings.value = locationSettings.value.copy(messageResId = messageResId)
_positionSettings.value = _positionSettings.value.copy(messageResId = messageResId)
} else {
val errorResId = R.string.location_qth_error
locationSettings.value = locationSettings.value.copy(messageResId = errorResId)
_positionSettings.value = _positionSettings.value.copy(messageResId = errorResId)
}
}
private fun dismissLocationMessage() {
locationSettings.value = locationSettings.value.copy(messageResId = 0)
fun dismissPosMessage() {
_positionSettings.value = _positionSettings.value.copy(messageResId = 0)
}
private fun setUtc(value: Boolean) {
settingsRepo.setUtcState(value)
otherSettings.value = otherSettings.value.copy(getUtc = value)
fun updateFromWeb() = viewModelScope.launch {
try {
_dataSettings.value = _dataSettings.value.copy(isUpdating = true)
databaseRepo.updateFromWeb()
} catch (exception: Exception) {
_dataSettings.value = _dataSettings.value.copy(isUpdating = false)
println(exception.printStackTrace())
}
}
private fun setUpdate(value: Boolean) {
settingsRepo.setUpdateState(value)
otherSettings.value = otherSettings.value.copy(getUpdate = value)
fun updateFromFile(uri: String) = viewModelScope.launch {
try {
_dataSettings.value = _dataSettings.value.copy(isUpdating = true)
databaseRepo.updateFromFile(uri)
} catch (exception: Exception) {
_dataSettings.value = _dataSettings.value.copy(isUpdating = false)
println(exception.printStackTrace())
}
}
private fun setSweep(value: Boolean) {
settingsRepo.setSweepState(value)
otherSettings.value = otherSettings.value.copy(getSweep = value)
}
fun clearAllData() = viewModelScope.launch { databaseRepo.clearAllData() }
private fun setSensor(value: Boolean) {
settingsRepo.setSensorState(value)
otherSettings.value = otherSettings.value.copy(getSensor = value)
}
fun toggleUtc(value: Boolean) = settingsRepo.toggleUtc(value)
fun toggleUpdate(value: Boolean) = settingsRepo.toggleUpdate(value)
fun toggleSweep(value: Boolean) = settingsRepo.toggleSweep(value)
fun toggleSensor(value: Boolean) = settingsRepo.toggleSensor(value)
// fun getRotatorEnabled(): Boolean = settings.getRotatorEnabled()
// fun setRotatorEnabled(value: Boolean) = settings.setRotatorEnabled(value)