mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Further classes restructuring and code cleanup
This commit is contained in:
1 parent
f6fcd21404
commit
a807cfd8f6
20 files changed
+292
-329
No files matched your search
@@ -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
|
||||
)
|
||||
+56
-69
@@ -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)
|
||||
|
||||
@@ -19,112 +19,85 @@ package com.rtbishop.look4sat.data
|
||||
|
||||
import com.rtbishop.look4sat.domain.IDatabaseRepo
|
||||
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 kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
class DatabaseRepo(
|
||||
private val dispatcher: CoroutineDispatcher,
|
||||
private val dataParser: DataParser,
|
||||
private val localStorage: ILocalStorage,
|
||||
private val dataSource: IDataSource,
|
||||
private val repositoryScope: CoroutineScope,
|
||||
private val localStorage: ILocalStorage,
|
||||
private val settingsRepository: ISettingsRepo
|
||||
) : IDatabaseRepo {
|
||||
|
||||
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||
_updateState.value = DataState.Exception(exception.message)
|
||||
}
|
||||
private val updateStateDelay = 875L
|
||||
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 suspend fun updateFromFile(uri: String) = withContext(dispatcher) {
|
||||
val importedSatellites = dataSource.getFileStream(uri)?.let { importSatellites(it) }
|
||||
importedSatellites?.let { localStorage.insertEntries(it) }
|
||||
setUpdateSuccessful(System.currentTimeMillis())
|
||||
}
|
||||
|
||||
override fun updateFromWeb() {
|
||||
_updateState.value = DataState.Loading
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
val importedEntries = mutableListOf<SatEntry>()
|
||||
val sourcesMap = settingsRepository.satelliteSourcesMap
|
||||
val jobsMap = sourcesMap.mapValues { async { dataSource.getNetworkStream(it.value) } }
|
||||
jobsMap.mapValues { job -> job.value.await() }.forEach { entry ->
|
||||
entry.value?.let { stream ->
|
||||
when (val type = entry.key) {
|
||||
"Amsat", "R4UAB" -> {
|
||||
// parse tle stream
|
||||
val satellites = importSatellites(stream)
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
settingsRepository.saveSatType(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
override suspend fun updateFromWeb() = withContext(dispatcher) {
|
||||
val sourcesMap = settingsRepository.satelliteSourcesMap
|
||||
val importedEntries = mutableListOf<SatEntry>()
|
||||
val importedRadios = mutableListOf<SatRadio>()
|
||||
// fetch
|
||||
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 ->
|
||||
entry.value?.let { stream ->
|
||||
when (val type = entry.key) {
|
||||
"AMSAT", "R4UAB" -> {
|
||||
// parse tle stream
|
||||
val satellites = importSatellites(stream)
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
settingsRepository.saveSatType(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
|
||||
"McCants", "Classified" -> {
|
||||
// unzip and parse tle stream
|
||||
val unzipped = ZipInputStream(stream).apply { nextEntry }
|
||||
val satellites = importSatellites(unzipped)
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
settingsRepository.saveSatType(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
"McCants", "Classified" -> {
|
||||
// unzip and parse tle stream
|
||||
val unzipped = ZipInputStream(stream).apply { nextEntry }
|
||||
val satellites = importSatellites(unzipped)
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
settingsRepository.saveSatType(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// parse csv stream
|
||||
val parsed = dataParser.parseCSVStream(stream)
|
||||
val satellites = parsed.map { data -> SatEntry(data) }
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
settingsRepository.saveSatType(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
else -> {
|
||||
// parse csv stream
|
||||
val parsed = dataParser.parseCSVStream(stream)
|
||||
val satellites = parsed.map { data -> SatEntry(data) }
|
||||
val catnums = satellites.map { it.data.catnum }
|
||||
settingsRepository.saveSatType(type, catnums)
|
||||
importedEntries.addAll(satellites)
|
||||
}
|
||||
}
|
||||
}
|
||||
localStorage.insertEntries(importedEntries)
|
||||
setUpdateSuccessful()
|
||||
}
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
dataSource.getNetworkStream(settingsRepository.radioSourceUrl)?.let { stream ->
|
||||
localStorage.insertRadios(dataParser.parseJSONStream(stream))
|
||||
}
|
||||
}
|
||||
jobRadios.await()?.let { importedRadios.addAll(dataParser.parseJSONStream(it)) }
|
||||
// insert
|
||||
localStorage.insertEntries(importedEntries)
|
||||
localStorage.insertRadios(importedRadios)
|
||||
setUpdateSuccessful(System.currentTimeMillis())
|
||||
}
|
||||
|
||||
override fun setUpdateStateHandled() {
|
||||
_updateState.value = DataState.Handled
|
||||
override suspend fun clearAllData() = withContext(dispatcher) {
|
||||
localStorage.deleteEntries()
|
||||
localStorage.deleteRadios()
|
||||
setUpdateSuccessful(0L)
|
||||
}
|
||||
|
||||
override fun clearAllData() {
|
||||
repositoryScope.launch {
|
||||
_updateState.value = DataState.Loading
|
||||
delay(updateStateDelay)
|
||||
localStorage.deleteEntries()
|
||||
localStorage.deleteRadios()
|
||||
setUpdateSuccessful(0L)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpdateSuccessful(updateTime: Long = System.currentTimeMillis()) {
|
||||
settingsRepository.setLastUpdateTime(updateTime)
|
||||
_updateState.value = DataState.Success(updateTime)
|
||||
private suspend fun setUpdateSuccessful(timestamp: Long) = withContext(dispatcher) {
|
||||
val satellitesTotal = localStorage.getEntriesTotal()
|
||||
val radiosTotal = localStorage.getRadiosTotal()
|
||||
settingsRepository.saveDatabaseState(DatabaseState(satellitesTotal, radiosTotal, timestamp))
|
||||
}
|
||||
|
||||
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.SatItem
|
||||
import com.rtbishop.look4sat.model.SatRadio
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ILocalStorage {
|
||||
|
||||
fun getEntriesTotal(): Flow<Int>
|
||||
suspend fun getEntriesTotal(): Int
|
||||
|
||||
suspend fun getEntriesList(): List<SatItem>
|
||||
|
||||
@@ -35,7 +34,7 @@ interface ILocalStorage {
|
||||
|
||||
suspend fun deleteEntries()
|
||||
|
||||
fun getRadiosTotal(): Flow<Int>
|
||||
suspend fun getRadiosTotal(): Int
|
||||
|
||||
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||
|
||||
|
||||
@@ -17,23 +17,11 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.DataState
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface IDatabaseRepo {
|
||||
|
||||
val updateState: StateFlow<DataState<Long>>
|
||||
suspend fun updateFromFile(uri: String)
|
||||
|
||||
fun getEntriesTotal(): Flow<Int>
|
||||
suspend fun updateFromWeb()
|
||||
|
||||
fun getRadiosTotal(): Flow<Int>
|
||||
|
||||
fun updateFromFile(uri: String)
|
||||
|
||||
fun updateFromWeb()
|
||||
|
||||
fun setUpdateStateHandled()
|
||||
|
||||
fun clearAllData()
|
||||
suspend fun clearAllData()
|
||||
}
|
||||
@@ -17,7 +17,9 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.model.DatabaseState
|
||||
import com.rtbishop.look4sat.model.GeoPos
|
||||
import com.rtbishop.look4sat.model.OtherSettings
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ISettingsRepo {
|
||||
@@ -72,25 +74,19 @@ interface ISettingsRepo {
|
||||
|
||||
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 setSensorState(value: Boolean)
|
||||
|
||||
fun isSweepEnabled(): Boolean
|
||||
|
||||
fun setSweepState(value: Boolean)
|
||||
fun toggleSensor(value: Boolean)
|
||||
|
||||
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
@@ -6,13 +6,13 @@ buildscript {
|
||||
kotlin_android_version = '1.8.10'
|
||||
// Android
|
||||
splashscreen_version = '1.0.0'
|
||||
room_version = '2.5.0'
|
||||
room_version = '2.5.1'
|
||||
// Compose
|
||||
activity_compose_version = '1.6.1'
|
||||
compose_version = '1.3.3'
|
||||
compose_compiler_version = '1.4.3'
|
||||
activity_compose_version = '1.7.0'
|
||||
compose_version = '1.4.0'
|
||||
compose_compiler_version = '1.4.4'
|
||||
material3_version = '1.0.1'
|
||||
lifecycle_version = '2.6.0'
|
||||
lifecycle_version = '2.6.1'
|
||||
navigation_version = '2.5.3'
|
||||
// Utility
|
||||
osmdroid_version = '6.1.14'
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
android.enableJetifier=false
|
||||
android.nonTransitiveRClass=true
|
||||
android.useAndroidX=true
|
||||
kapt.include.compile.classpath=true
|
||||
kapt.incremental.apt=true
|
||||
kapt.use.worker.api=true
|
||||
kotlin.code.style=official
|
||||
kotlin.daemon.jvm.options=-Xmx4096M
|
||||
kotlin.incremental=true
|
||||
|
||||
Reference in new issue
Block a user