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
+262 -299

No files matched your search

-1
View File
@@ -65,7 +65,6 @@ dependencies {
implementation "androidx.compose.compiler:compiler:$compose_compiler_version" implementation "androidx.compose.compiler:compiler:$compose_compiler_version"
implementation "androidx.compose.material3:material3:$material3_version" implementation "androidx.compose.material3:material3:$material3_version"
implementation "androidx.compose.runtime:runtime:$compose_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.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
implementation "androidx.navigation:navigation-compose:$navigation_version" implementation "androidx.navigation:navigation-compose:$navigation_version"
@@ -18,6 +18,7 @@
package com.rtbishop.look4sat package com.rtbishop.look4sat
import android.app.Application import android.app.Application
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@@ -29,13 +30,13 @@ class MainApplication : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
container = MainContainer(this) container = MainContainer(this)
checkAutoUpdate() container.mainScope.launch { checkAutoUpdate() }
} }
private fun checkAutoUpdate() { private suspend fun checkAutoUpdate() {
val settingsRepo = container.settingsRepo val settingsRepo = container.settingsRepo
if (settingsRepo.isUpdateEnabled()) { if (settingsRepo.otherSettings.value.updateState) {
val timeDelta = System.currentTimeMillis() - settingsRepo.getLastUpdateTime() val timeDelta = System.currentTimeMillis() - settingsRepo.databaseState.value.timestamp
if (timeDelta > 172800000) { // 48 hours in ms if (timeDelta > 172800000) { // 48 hours in ms
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault()) val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
println("Started periodic data update on ${sdf.format(Date())}") 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.LocalStorage
import com.rtbishop.look4sat.framework.data.MIGRATION_1_2 import com.rtbishop.look4sat.framework.data.MIGRATION_1_2
import com.rtbishop.look4sat.framework.data.MainDatabase import com.rtbishop.look4sat.framework.data.MainDatabase
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@@ -33,10 +34,13 @@ class MainContainer(private val context: Context) {
private val database = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb") private val database = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb")
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build() .addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
private val localStorage = LocalStorage(database.storageDao()) 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 settingsRepo = provideSettingsRepo()
val databaseRepo = provideDatabaseRepo(settingsRepo) val databaseRepo = provideDatabaseRepo()
val sensorsRepo = provideSensorRepo()
val satelliteRepo = provideSatelliteRepo() val satelliteRepo = provideSatelliteRepo()
val selectionRepo = provideSelectionRepo()
val sensorsRepo = provideSensorsRepo()
fun provideBluetoothReporter(): BluetoothReporter { fun provideBluetoothReporter(): BluetoothReporter {
val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
@@ -47,27 +51,26 @@ class MainContainer(private val context: Context) {
return NetworkReporter(CoroutineScope(Dispatchers.IO)) return NetworkReporter(CoroutineScope(Dispatchers.IO))
} }
private fun provideDatabaseRepo(settingsRepo: ISettingsRepo): IDatabaseRepo { private fun provideDatabaseRepo(): IDatabaseRepo {
val dataParser = DataParser(Dispatchers.Default) val dataParser = DataParser(Dispatchers.Default)
val dataSource = DataSource(context.contentResolver, Dispatchers.IO) val dataSource = DataSource(context.contentResolver, Dispatchers.IO)
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) return DatabaseRepo(Dispatchers.Default, dataParser, dataSource, localStorage, settingsRepo)
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)
} }
private fun provideSatelliteRepo(): ISatelliteRepo { private fun provideSatelliteRepo(): ISatelliteRepo {
return SatelliteRepo(Dispatchers.Default, localStorage) 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 { private fun provideSettingsRepo(): ISettingsRepo {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE) 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.location.LocationManagerCompat
import androidx.core.os.CancellationSignal import androidx.core.os.CancellationSignal
import com.rtbishop.look4sat.domain.ISettingsRepo import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.DatabaseState
import com.rtbishop.look4sat.model.GeoPos import com.rtbishop.look4sat.model.GeoPos
import com.rtbishop.look4sat.model.OtherSettings
import com.rtbishop.look4sat.utility.QthConverter import com.rtbishop.look4sat.utility.QthConverter
import com.rtbishop.look4sat.utility.round import com.rtbishop.look4sat.utility.round
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -36,11 +38,13 @@ class SettingsRepo(
) : ISettingsRepo { ) : ISettingsRepo {
private val keyModes = "satModes" private val keyModes = "satModes"
private val keyCompass = "compass" private val keyUtcState = "utcState"
private val keyRadarSweep = "radarSweep" private val keyUpdateState = "updateState"
private val keyTimeUTC = "timeUTC" private val keySweepState = "sweepState"
private val keyLastUpdateTime = "lastUpdateTime" private val keySensorState = "sensorState"
private val keyAutoUpdateEnabled = "autoUpdateEnabled" private val keyDataEntries = "dataEntries"
private val keyDataRadios = "dataRadios"
private val keyDataTimestamp = "dataTimestamp"
private val keyHoursAhead = "hoursAhead" private val keyHoursAhead = "hoursAhead"
private val keyMinElevation = "minElevation" private val keyMinElevation = "minElevation"
private val keyRotator = "isRotatorEnabled" private val keyRotator = "isRotatorEnabled"
@@ -164,44 +168,52 @@ class SettingsRepo(
preferences.edit { putDouble(keyMinElevation, minElevation) } preferences.edit { putDouble(keyMinElevation, minElevation) }
} }
override fun isUtcEnabled(): Boolean { private val _databaseState = MutableStateFlow(loadDatabaseState())
return preferences.getBoolean(keyTimeUTC, false) 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) { private fun loadDatabaseState(): DatabaseState {
preferences.edit { putBoolean(keyTimeUTC, value) } 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 { private val _otherSettings = MutableStateFlow(loadOtherSettings())
return preferences.getLong(keyLastUpdateTime, 0L) 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) { override fun toggleUpdate(value: Boolean) {
preferences.edit { putLong(keyLastUpdateTime, updateTime) } preferences.edit { putBoolean(keyUpdateState, value) }
_otherSettings.value = otherSettings.value.copy(updateState = value)
} }
override fun isUpdateEnabled(): Boolean { override fun toggleSweep(value: Boolean) {
return preferences.getBoolean(keyAutoUpdateEnabled, true) preferences.edit { putBoolean(keySweepState, value) }
_otherSettings.value = otherSettings.value.copy(sweepState = value)
} }
override fun setUpdateState(value: Boolean) { override fun toggleSensor(value: Boolean) {
preferences.edit { putBoolean(keyAutoUpdateEnabled, value) } preferences.edit { putBoolean(keySensorState, value) }
_otherSettings.value = otherSettings.value.copy(sensorState = value)
} }
override fun isSensorEnabled(): Boolean { private fun loadOtherSettings(): OtherSettings {
return preferences.getBoolean(keyCompass, true) val utcState = preferences.getBoolean(keyUtcState, false)
} val updateState = preferences.getBoolean(keyUpdateState, true)
val sweepState = preferences.getBoolean(keySweepState, true)
override fun setSensorState(value: Boolean) { val sensorState = preferences.getBoolean(keySensorState, true)
preferences.edit { putBoolean(keyCompass, value) } return OtherSettings(utcState, updateState, sweepState, sensorState)
}
override fun isSweepEnabled(): Boolean {
return preferences.getBoolean(keyRadarSweep, true)
}
override fun setSweepState(value: Boolean) {
preferences.edit { putBoolean(keyRadarSweep, value) }
} }
override fun saveModesSelection(modes: List<String>) { override fun saveModesSelection(modes: List<String>) {
@@ -31,7 +31,7 @@ class LocalStorage(private val storageDao: StorageDao) : ILocalStorage {
//region # Entries region //region # Entries region
override fun getEntriesTotal() = storageDao.getEntriesTotal() override suspend fun getEntriesTotal() = storageDao.getEntriesTotal()
override suspend fun getEntriesList(): List<DomainItem> { override suspend fun getEntriesList(): List<DomainItem> {
return storageDao.getEntriesList().toDomainItems() return storageDao.getEntriesList().toDomainItems()
@@ -64,7 +64,7 @@ class LocalStorage(private val storageDao: StorageDao) : ILocalStorage {
//region # Radios region //region # Radios region
override fun getRadiosTotal() = storageDao.getRadiosTotal() override suspend fun getRadiosTotal() = storageDao.getRadiosTotal()
override suspend fun getRadiosWithId(id: Int): List<SatRadio> { override suspend fun getRadiosWithId(id: Int): List<SatRadio> {
return storageDao.getRadiosWithId(id).toDomainRadios() 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.SatEntry
import com.rtbishop.look4sat.framework.model.SatRadio 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 class MainDatabase : RoomDatabase() {
abstract fun storageDao(): StorageDao abstract fun storageDao(): StorageDao
@@ -21,13 +21,12 @@ import androidx.room.*
import com.rtbishop.look4sat.framework.model.SatEntry import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.SatItem import com.rtbishop.look4sat.framework.model.SatItem
import com.rtbishop.look4sat.framework.model.SatRadio import com.rtbishop.look4sat.framework.model.SatRadio
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface StorageDao { interface StorageDao {
@Query("SELECT COUNT(*) FROM entries") @Query("SELECT COUNT(*) FROM entries")
fun getEntriesTotal(): Flow<Int> suspend fun getEntriesTotal(): Int
//@Transaction //@Transaction
@Query("SELECT catnum, name FROM entries ORDER BY name ASC") @Query("SELECT catnum, name FROM entries ORDER BY name ASC")
@@ -44,7 +43,7 @@ interface StorageDao {
suspend fun deleteEntries() suspend fun deleteEntries()
@Query("SELECT COUNT(*) FROM radios") @Query("SELECT COUNT(*) FROM radios")
fun getRadiosTotal(): Flow<Int> suspend fun getRadiosTotal(): Int
@Transaction @Transaction
@Query("SELECT * FROM radios WHERE catnum = :id AND isAlive = 1") @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 val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer { initializer {
val container = (this[applicationKey] as MainApplication).container 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( fun calculatePasses(
hoursAhead: Int = 1, hoursAhead: Int = 1,
@@ -61,7 +61,7 @@ class RadarViewModel(
val orientation = mutableStateOf(sensorsRepo.orientation.value) val orientation = mutableStateOf(sensorsRepo.orientation.value)
init { init {
if (settingsRepo.isSensorEnabled()) { if (settingsRepo.otherSettings.value.sensorState) {
viewModelScope.launch { viewModelScope.launch {
sensorsRepo.enableSensor() sensorsRepo.enableSensor()
sensorsRepo.orientation.collect { data -> 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 { private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
val latitude = geoPos.latitude.toFloat() val latitude = geoPos.latitude.toFloat()
@@ -38,6 +38,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.rtbishop.look4sat.BuildConfig import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.model.GeoPos import com.rtbishop.look4sat.model.GeoPos
import com.rtbishop.look4sat.model.OtherSettings
import com.rtbishop.look4sat.presentation.CardButton import com.rtbishop.look4sat.presentation.CardButton
import com.rtbishop.look4sat.presentation.MainTheme import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.dialogs.LocatorDialog import com.rtbishop.look4sat.presentation.dialogs.LocatorDialog
@@ -76,43 +77,55 @@ fun SettingsScreen() {
val locationPermFine = Manifest.permission.ACCESS_FINE_LOCATION val locationPermFine = Manifest.permission.ACCESS_FINE_LOCATION
val locationRequest = rememberLauncherForActivityResult(locationContract) { permissions -> val locationRequest = rememberLauncherForActivityResult(locationContract) { permissions ->
when { when {
permissions[locationPermFine] == true -> viewModel.locationSettings.value.setGpsLoc() permissions[locationPermFine] == true -> viewModel.setGpsPosition()
permissions[locationPermCoarse] == true -> viewModel.locationSettings.value.setGpsLoc() permissions[locationPermCoarse] == true -> viewModel.setGpsPosition()
else -> showToast(context, locationError) else -> showToast(context, locationError)
} }
} }
val contentContract = ActivityResultContracts.GetContent() val contentContract = ActivityResultContracts.GetContent()
val contentRequest = rememberLauncherForActivityResult(contentContract) { uri -> val contentRequest = rememberLauncherForActivityResult(contentContract) { uri ->
uri?.let { viewModel.dataSettings.value.updateFromFile(uri.toString()) } uri?.let { viewModel.updateFromFile(uri.toString()) }
} }
// Location settings // Position settings
val locSettings = viewModel.locationSettings.value val positionSettings = viewModel.positionSettings.value
val setGpsLoc = { locationRequest.launch(arrayOf(locationPermCoarse, locationPermFine)) } val stationPos = positionSettings.stationPos
val showPosDialog = rememberSaveable { mutableStateOf(false) } val setGpsPos = { locationRequest.launch(arrayOf(locationPermCoarse, locationPermFine)) }
val togglePosDialog = { showPosDialog.value = showPosDialog.value.not() } val setGeoPos = { lat: Double, lon: Double -> viewModel.setGeoPosition(lat, lon) }
if (showPosDialog.value) { val setQthPos = { locator: String -> viewModel.setQthPosition(locator) }
PositionDialog( val dismissPos = { viewModel.dismissPosMessage() }
locSettings.stationPos.latitude, val posDialogState = rememberSaveable { mutableStateOf(false) }
locSettings.stationPos.longitude, val showPosDialog = { posDialogState.value = posDialogState.value.not() }
togglePosDialog, if (posDialogState.value) {
locSettings.setManualLoc PositionDialog(stationPos.latitude, stationPos.longitude, showPosDialog, setGeoPos)
)
} }
val showLocDialog = rememberSaveable { mutableStateOf(false) } val locDialogState = rememberSaveable { mutableStateOf(false) }
val toggleLocDialog = { showLocDialog.value = showLocDialog.value.not() } val showLocDialog = { locDialogState.value = locDialogState.value.not() }
if (showLocDialog.value) { if (locDialogState.value) {
LocatorDialog(locSettings.stationPos.qthLocator, toggleLocDialog, locSettings.setQthLoc) 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 // Screen setup
LazyColumn( LazyColumn(
modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp) modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)
) { ) {
item { CardAbout(BuildConfig.VERSION_NAME) } item { CardAbout(BuildConfig.VERSION_NAME) }
item { LocationCard(locSettings, setGpsLoc, togglePosDialog, toggleLocDialog) } item { LocationCard(positionSettings, setGpsPos, showPosDialog, showLocDialog, dismissPos) }
item { DataCard(viewModel.dataSettings.value) { contentRequest.launch("*/*") } } item { DataCard(dataSettings, updateFromWeb, updateFromFile, clearAllData) }
item { OtherCard(viewModel.otherSettings.value) } item { OtherCard(otherSettings, toggleUtc, toggleUpdate, toggleSweep, toggleSensor) }
item { CardCredits() } item { CardCredits() }
} }
} }
@@ -181,16 +194,17 @@ private fun CardAbout(version: String, modifier: Modifier = Modifier) {
@Composable @Composable
private fun LocationCardPreview() = MainTheme { private fun LocationCardPreview() = MainTheme {
val stationPos = GeoPos(0.0, 0.0, 0.0, "IO91vl", 0L) val stationPos = GeoPos(0.0, 0.0, 0.0, "IO91vl", 0L)
val settings = LocationSettings(true, stationPos, 0, {}, { _, _ -> }, {}, {}) val settings = PositionSettings(true, stationPos, 0)
LocationCard(settings = settings, setGpsLoc = {}, togglePosDialog = {}) {} LocationCard(settings = settings, setGpsPos = {}, showPosDialog = {}, {}) {}
} }
@Composable @Composable
private fun LocationCard( private fun LocationCard(
settings: LocationSettings, settings: PositionSettings,
setGpsLoc: () -> Unit, setGpsPos: () -> Unit,
togglePosDialog: () -> Unit, showPosDialog: () -> Unit,
toggleLocDialog: () -> Unit showLocDialog: () -> Unit,
dismissPosMessage: () -> Unit
) { ) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) { ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column( Column(
@@ -227,19 +241,19 @@ private fun LocationCard(
Spacer(modifier = Modifier.height(1.dp)) Spacer(modifier = Modifier.height(1.dp))
Row(horizontalArrangement = Arrangement.SpaceEvenly) { Row(horizontalArrangement = Arrangement.SpaceEvenly) {
CardButton( CardButton(
onClick = setGpsLoc, onClick = setGpsPos,
text = stringResource(id = R.string.btn_gps), text = stringResource(id = R.string.btn_gps),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
Spacer(modifier = Modifier.width(6.dp)) Spacer(modifier = Modifier.width(6.dp))
CardButton( CardButton(
onClick = togglePosDialog, onClick = showPosDialog,
text = stringResource(id = R.string.btn_manual), text = stringResource(id = R.string.btn_manual),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
Spacer(modifier = Modifier.width(6.dp)) Spacer(modifier = Modifier.width(6.dp))
CardButton( CardButton(
onClick = toggleLocDialog, onClick = showLocDialog,
text = stringResource(id = R.string.btn_qth), text = stringResource(id = R.string.btn_qth),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
@@ -251,7 +265,7 @@ private fun LocationCard(
val errorString = stringResource(id = settings.messageResId) val errorString = stringResource(id = settings.messageResId)
LaunchedEffect(key1 = settings.messageResId) { LaunchedEffect(key1 = settings.messageResId) {
showToast(context, errorString) showToast(context, errorString)
settings.dismissMessage() dismissPosMessage()
} }
} }
} }
@@ -259,12 +273,17 @@ private fun LocationCard(
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun DataCardPreview() = MainTheme { private fun DataCardPreview() = MainTheme {
val settings = DataSettings(true, 0L, 5000, 2500, {}, {}, {}) val settings = DataSettings(true, 5000, 2500, 0L)
DataCard(settings = settings) {} DataCard(settings = settings, {}, {}) {}
} }
@Composable @Composable
private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) { private fun DataCard(
settings: DataSettings,
updateFromWeb: () -> Unit,
updateFromFile: () -> Unit,
clearAllData: () -> Unit
) {
ElevatedCard(modifier = Modifier.fillMaxWidth()) { ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
@@ -278,29 +297,29 @@ private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) {
UpdateIndicator(isUpdating = settings.isUpdating, Modifier.weight(1f)) UpdateIndicator(isUpdating = settings.isUpdating, Modifier.weight(1f))
} }
Spacer(modifier = Modifier.height(2.dp)) Spacer(modifier = Modifier.height(2.dp))
Text(text = setUpdateTime(updateTime = settings.lastUpdated)) Text(text = setUpdateTime(updateTime = settings.timestamp))
Spacer(modifier = Modifier.height(2.dp)) Spacer(modifier = Modifier.height(2.dp))
Row(horizontalArrangement = Arrangement.SpaceBetween) { Row(horizontalArrangement = Arrangement.SpaceBetween) {
Text(text = "Satellites: ${settings.satsTotal}") Text(text = "Satellites: ${settings.entriesTotal}")
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Text(text = "Transceivers: ${settings.radiosTotal}") Text(text = "Transceivers: ${settings.radiosTotal}")
} }
Spacer(modifier = Modifier.height(1.dp)) Spacer(modifier = Modifier.height(1.dp))
Row(horizontalArrangement = Arrangement.SpaceEvenly) { Row(horizontalArrangement = Arrangement.SpaceEvenly) {
CardButton( CardButton(
onClick = settings.updateFromWeb, onClick = { updateFromWeb() },
text = stringResource(id = R.string.btn_web), text = stringResource(id = R.string.btn_web),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
Spacer(modifier = Modifier.width(6.dp)) Spacer(modifier = Modifier.width(6.dp))
CardButton( CardButton(
onClick = updateFromFile, onClick = { updateFromFile() },
text = stringResource(id = R.string.btn_file), text = stringResource(id = R.string.btn_file),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
Spacer(modifier = Modifier.width(6.dp)) Spacer(modifier = Modifier.width(6.dp))
CardButton( CardButton(
onClick = settings.clearAllData, onClick = { clearAllData() },
text = stringResource(id = R.string.btn_clear), text = stringResource(id = R.string.btn_clear),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
@@ -357,14 +376,19 @@ private fun DataCard(settings: DataSettings, updateFromFile: () -> Unit) {
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun OtherCardPreview() = MainTheme { private fun OtherCardPreview() = MainTheme {
val settings = OtherSettings( val values =
getUtc = false, getUpdate = true, getSweep = true, getSensor = true, OtherSettings(utcState = false, updateState = true, sweepState = true, sensorState = true)
setUtc = {}, setUpdate = {}, setSweep = {}, setSensor = {}) OtherCard(settings = values, {}, {}, {}, {})
OtherCard(settings = settings)
} }
@Composable @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()) { ElevatedCard(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)) { Column(modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)) {
Row( Row(
@@ -373,7 +397,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(text = stringResource(id = R.string.other_switch_utc)) 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( Row(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
@@ -381,7 +405,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(text = stringResource(id = R.string.other_switch_update)) 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( Row(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
@@ -389,7 +413,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(text = stringResource(id = R.string.other_switch_sweep)) 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( Row(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
@@ -397,7 +421,7 @@ private fun OtherCard(settings: OtherSettings) {
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(text = stringResource(id = R.string.other_switch_sensors)) 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 import com.rtbishop.look4sat.model.GeoPos
data class LocationSettings( data class PositionSettings(val isUpdating: Boolean, val stationPos: GeoPos, val messageResId: Int)
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 DataSettings( data class DataSettings(
val isUpdating: Boolean, val isUpdating: Boolean, val entriesTotal: Int, val radiosTotal: Int, val timestamp: Long
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
) )
@@ -17,6 +17,7 @@
*/ */
package com.rtbishop.look4sat.presentation.settings package com.rtbishop.look4sat.presentation.settings
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@@ -27,121 +28,107 @@ import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.IDatabaseRepo import com.rtbishop.look4sat.domain.IDatabaseRepo
import com.rtbishop.look4sat.domain.ISettingsRepo import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.DataState import com.rtbishop.look4sat.model.OtherSettings
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class SettingsViewModel( class SettingsViewModel(
private val databaseRepo: IDatabaseRepo, private val settingsRepo: ISettingsRepo private val databaseRepo: IDatabaseRepo, private val settingsRepo: ISettingsRepo
) : ViewModel() { ) : ViewModel() {
private val defaultLocationSettings = LocationSettings(false, private val defaultPosSettings = PositionSettings(false, settingsRepo.stationPosition.value, 0)
settingsRepo.stationPosition.value, private val _positionSettings = mutableStateOf(defaultPosSettings)
0, val positionSettings: State<PositionSettings> = _positionSettings
{ setGpsPosition() },
{ latitude, longitude -> setGeoPosition(latitude, longitude) },
{ locator -> setQthPosition(locator) },
{ dismissLocationMessage() })
val locationSettings = mutableStateOf(defaultLocationSettings)
private val defaultDataSettings = DataSettings(false, private val defaultDataSettings = DataSettings(false, 0, 0, 0L)
settingsRepo.getLastUpdateTime(), private val _dataSettings = mutableStateOf(defaultDataSettings)
0, val dataSettings: State<DataSettings> = _dataSettings
0,
{ databaseRepo.updateFromWeb() },
{ databaseRepo.updateFromFile(it) },
{ databaseRepo.clearAllData() })
val dataSettings = mutableStateOf(defaultDataSettings)
private val defaultOtherSettings = OtherSettings(settingsRepo.isUtcEnabled(), private val _otherSettings = mutableStateOf(settingsRepo.otherSettings.value)
settingsRepo.isUpdateEnabled(), val otherSettings: State<OtherSettings> = _otherSettings
settingsRepo.isSweepEnabled(),
settingsRepo.isSensorEnabled(),
{ setUtc(it) },
{ setUpdate(it) },
{ setSweep(it) },
{ setSensor(it) })
val otherSettings = mutableStateOf(defaultOtherSettings)
init { init {
viewModelScope.launch { viewModelScope.launch {
settingsRepo.stationPosition.collect { stationPos -> settingsRepo.stationPosition.collect { geoPos ->
locationSettings.value = locationSettings.value.copy( _positionSettings.value =
isUpdating = false, stationPos = stationPos _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 { viewModelScope.launch {
databaseRepo.updateState.collect { settingsRepo.otherSettings.collect { _otherSettings.value = it }
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 {}
} }
} }
private fun setGpsPosition() { fun setGpsPosition() {
if (settingsRepo.setGpsPosition()) { if (settingsRepo.setGpsPosition()) {
val messageResId = R.string.location_success val messageResId = R.string.location_success
locationSettings.value = _positionSettings.value =
locationSettings.value.copy(isUpdating = true, messageResId = messageResId) _positionSettings.value.copy(isUpdating = true, messageResId = messageResId)
} else { } else {
val errorResId = R.string.location_gps_error 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)) { if (settingsRepo.setGeoPosition(latitude, longitude)) {
val messageResId = R.string.location_success val messageResId = R.string.location_success
locationSettings.value = locationSettings.value.copy(messageResId = messageResId) _positionSettings.value = _positionSettings.value.copy(messageResId = messageResId)
} else { } else {
val errorResId = R.string.location_manual_error 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)) { if (settingsRepo.setQthPosition(locator)) {
val messageResId = R.string.location_success val messageResId = R.string.location_success
locationSettings.value = locationSettings.value.copy(messageResId = messageResId) _positionSettings.value = _positionSettings.value.copy(messageResId = messageResId)
} else { } else {
val errorResId = R.string.location_qth_error val errorResId = R.string.location_qth_error
locationSettings.value = locationSettings.value.copy(messageResId = errorResId) _positionSettings.value = _positionSettings.value.copy(messageResId = errorResId)
} }
} }
private fun dismissLocationMessage() { fun dismissPosMessage() {
locationSettings.value = locationSettings.value.copy(messageResId = 0) _positionSettings.value = _positionSettings.value.copy(messageResId = 0)
} }
private fun setUtc(value: Boolean) { fun updateFromWeb() = viewModelScope.launch {
settingsRepo.setUtcState(value) try {
otherSettings.value = otherSettings.value.copy(getUtc = value) _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) { fun updateFromFile(uri: String) = viewModelScope.launch {
settingsRepo.setUpdateState(value) try {
otherSettings.value = otherSettings.value.copy(getUpdate = value) _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) { fun clearAllData() = viewModelScope.launch { databaseRepo.clearAllData() }
settingsRepo.setSweepState(value)
otherSettings.value = otherSettings.value.copy(getSweep = value)
}
private fun setSensor(value: Boolean) { fun toggleUtc(value: Boolean) = settingsRepo.toggleUtc(value)
settingsRepo.setSensorState(value) fun toggleUpdate(value: Boolean) = settingsRepo.toggleUpdate(value)
otherSettings.value = otherSettings.value.copy(getSensor = value) fun toggleSweep(value: Boolean) = settingsRepo.toggleSweep(value)
} fun toggleSensor(value: Boolean) = settingsRepo.toggleSensor(value)
// fun getRotatorEnabled(): Boolean = settings.getRotatorEnabled() // fun getRotatorEnabled(): Boolean = settings.getRotatorEnabled()
// fun setRotatorEnabled(value: Boolean) = settings.setRotatorEnabled(value) // fun setRotatorEnabled(value: Boolean) = settings.setRotatorEnabled(value)
@@ -19,58 +19,41 @@ package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.IDatabaseRepo import com.rtbishop.look4sat.domain.IDatabaseRepo
import com.rtbishop.look4sat.domain.ISettingsRepo import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.DataState import com.rtbishop.look4sat.model.DatabaseState
import com.rtbishop.look4sat.model.SatEntry import com.rtbishop.look4sat.model.SatEntry
import kotlinx.coroutines.CoroutineExceptionHandler import com.rtbishop.look4sat.model.SatRadio
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.delay import kotlinx.coroutines.withContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.io.InputStream import java.io.InputStream
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
class DatabaseRepo( class DatabaseRepo(
private val dispatcher: CoroutineDispatcher,
private val dataParser: DataParser, private val dataParser: DataParser,
private val localStorage: ILocalStorage,
private val dataSource: IDataSource, private val dataSource: IDataSource,
private val repositoryScope: CoroutineScope, private val localStorage: ILocalStorage,
private val settingsRepository: ISettingsRepo private val settingsRepository: ISettingsRepo
) : IDatabaseRepo { ) : IDatabaseRepo {
private val exceptionHandler = CoroutineExceptionHandler { _, exception -> override suspend fun updateFromFile(uri: String) = withContext(dispatcher) {
_updateState.value = DataState.Exception(exception.message) val importedSatellites = dataSource.getFileStream(uri)?.let { importSatellites(it) }
} importedSatellites?.let { localStorage.insertEntries(it) }
private val updateStateDelay = 875L setUpdateSuccessful(System.currentTimeMillis())
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
override val updateState: StateFlow<DataState<Long>> = _updateState
override fun getEntriesTotal() = localStorage.getEntriesTotal()
override fun getRadiosTotal() = localStorage.getRadiosTotal()
override fun updateFromFile(uri: String) {
repositoryScope.launch(exceptionHandler) {
_updateState.value = DataState.Loading
dataSource.getFileStream(uri)?.let { stream ->
delay(updateStateDelay)
localStorage.insertEntries(importSatellites(stream))
}
setUpdateSuccessful()
}
} }
override fun updateFromWeb() { override suspend fun updateFromWeb() = withContext(dispatcher) {
_updateState.value = DataState.Loading
repositoryScope.launch(exceptionHandler) {
val importedEntries = mutableListOf<SatEntry>()
val sourcesMap = settingsRepository.satelliteSourcesMap val sourcesMap = settingsRepository.satelliteSourcesMap
val importedEntries = mutableListOf<SatEntry>()
val importedRadios = mutableListOf<SatRadio>()
// fetch
val jobsMap = sourcesMap.mapValues { async { dataSource.getNetworkStream(it.value) } } val jobsMap = sourcesMap.mapValues { async { dataSource.getNetworkStream(it.value) } }
val jobRadios = async { dataSource.getNetworkStream(settingsRepository.radioSourceUrl) }
// parse
jobsMap.mapValues { job -> job.value.await() }.forEach { entry -> jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
entry.value?.let { stream -> entry.value?.let { stream ->
when (val type = entry.key) { when (val type = entry.key) {
"Amsat", "R4UAB" -> { "AMSAT", "R4UAB" -> {
// parse tle stream // parse tle stream
val satellites = importSatellites(stream) val satellites = importSatellites(stream)
val catnums = satellites.map { it.data.catnum } val catnums = satellites.map { it.data.catnum }
@@ -98,33 +81,23 @@ class DatabaseRepo(
} }
} }
} }
jobRadios.await()?.let { importedRadios.addAll(dataParser.parseJSONStream(it)) }
// insert
localStorage.insertEntries(importedEntries) localStorage.insertEntries(importedEntries)
setUpdateSuccessful() localStorage.insertRadios(importedRadios)
} setUpdateSuccessful(System.currentTimeMillis())
repositoryScope.launch(exceptionHandler) {
dataSource.getNetworkStream(settingsRepository.radioSourceUrl)?.let { stream ->
localStorage.insertRadios(dataParser.parseJSONStream(stream))
}
}
} }
override fun setUpdateStateHandled() { override suspend fun clearAllData() = withContext(dispatcher) {
_updateState.value = DataState.Handled
}
override fun clearAllData() {
repositoryScope.launch {
_updateState.value = DataState.Loading
delay(updateStateDelay)
localStorage.deleteEntries() localStorage.deleteEntries()
localStorage.deleteRadios() localStorage.deleteRadios()
setUpdateSuccessful(0L) setUpdateSuccessful(0L)
} }
}
private fun setUpdateSuccessful(updateTime: Long = System.currentTimeMillis()) { private suspend fun setUpdateSuccessful(timestamp: Long) = withContext(dispatcher) {
settingsRepository.setLastUpdateTime(updateTime) val satellitesTotal = localStorage.getEntriesTotal()
_updateState.value = DataState.Success(updateTime) val radiosTotal = localStorage.getRadiosTotal()
settingsRepository.saveDatabaseState(DatabaseState(satellitesTotal, radiosTotal, timestamp))
} }
private suspend fun importSatellites(stream: InputStream): List<SatEntry> { private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
@@ -21,11 +21,10 @@ import com.rtbishop.look4sat.domain.Satellite
import com.rtbishop.look4sat.model.SatEntry import com.rtbishop.look4sat.model.SatEntry
import com.rtbishop.look4sat.model.SatItem import com.rtbishop.look4sat.model.SatItem
import com.rtbishop.look4sat.model.SatRadio import com.rtbishop.look4sat.model.SatRadio
import kotlinx.coroutines.flow.Flow
interface ILocalStorage { interface ILocalStorage {
fun getEntriesTotal(): Flow<Int> suspend fun getEntriesTotal(): Int
suspend fun getEntriesList(): List<SatItem> suspend fun getEntriesList(): List<SatItem>
@@ -35,7 +34,7 @@ interface ILocalStorage {
suspend fun deleteEntries() suspend fun deleteEntries()
fun getRadiosTotal(): Flow<Int> suspend fun getRadiosTotal(): Int
suspend fun getRadiosWithId(id: Int): List<SatRadio> suspend fun getRadiosWithId(id: Int): List<SatRadio>
@@ -17,23 +17,11 @@
*/ */
package com.rtbishop.look4sat.domain package com.rtbishop.look4sat.domain
import com.rtbishop.look4sat.model.DataState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
interface IDatabaseRepo { interface IDatabaseRepo {
val updateState: StateFlow<DataState<Long>> suspend fun updateFromFile(uri: String)
fun getEntriesTotal(): Flow<Int> suspend fun updateFromWeb()
fun getRadiosTotal(): Flow<Int> suspend fun clearAllData()
fun updateFromFile(uri: String)
fun updateFromWeb()
fun setUpdateStateHandled()
fun clearAllData()
} }
@@ -17,7 +17,9 @@
*/ */
package com.rtbishop.look4sat.domain package com.rtbishop.look4sat.domain
import com.rtbishop.look4sat.model.DatabaseState
import com.rtbishop.look4sat.model.GeoPos import com.rtbishop.look4sat.model.GeoPos
import com.rtbishop.look4sat.model.OtherSettings
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
interface ISettingsRepo { interface ISettingsRepo {
@@ -72,25 +74,19 @@ interface ISettingsRepo {
fun setMinElevation(minElevation: Double) fun setMinElevation(minElevation: Double)
fun isUtcEnabled(): Boolean val databaseState: StateFlow<DatabaseState>
fun setUtcState(value: Boolean) fun saveDatabaseState(state: DatabaseState)
fun getLastUpdateTime(): Long val otherSettings: StateFlow<OtherSettings>
fun setLastUpdateTime(updateTime: Long) fun toggleUtc(value: Boolean)
fun isUpdateEnabled(): Boolean fun toggleUpdate(value: Boolean)
fun setUpdateState(value: Boolean) fun toggleSweep(value: Boolean)
fun isSensorEnabled(): Boolean fun toggleSensor(value: Boolean)
fun setSensorState(value: Boolean)
fun isSweepEnabled(): Boolean
fun setSweepState(value: Boolean)
fun saveModesSelection(modes: List<String>) fun saveModesSelection(modes: List<String>)
@@ -0,0 +1,10 @@
package com.rtbishop.look4sat.model
data class OtherSettings(
val utcState: Boolean,
val updateState: Boolean,
val sweepState: Boolean,
val sensorState: Boolean
)
data class DatabaseState(val entriesTotal: Int, val radiosTotal: Int, val timestamp: Long)
+5 -5
View File
@@ -6,13 +6,13 @@ buildscript {
kotlin_android_version = '1.8.10' kotlin_android_version = '1.8.10'
// Android // Android
splashscreen_version = '1.0.0' splashscreen_version = '1.0.0'
room_version = '2.5.0' room_version = '2.5.1'
// Compose // Compose
activity_compose_version = '1.6.1' activity_compose_version = '1.7.0'
compose_version = '1.3.3' compose_version = '1.4.0'
compose_compiler_version = '1.4.3' compose_compiler_version = '1.4.4'
material3_version = '1.0.1' material3_version = '1.0.1'
lifecycle_version = '2.6.0' lifecycle_version = '2.6.1'
navigation_version = '2.5.3' navigation_version = '2.5.3'
// Utility // Utility
osmdroid_version = '6.1.14' osmdroid_version = '6.1.14'
-3
View File
@@ -24,9 +24,6 @@
android.enableJetifier=false android.enableJetifier=false
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.useAndroidX=true android.useAndroidX=true
kapt.include.compile.classpath=true
kapt.incremental.apt=true
kapt.use.worker.api=true
kotlin.code.style=official kotlin.code.style=official
kotlin.daemon.jvm.options=-Xmx4096M kotlin.daemon.jvm.options=-Xmx4096M
kotlin.incremental=true kotlin.incremental=true