Simplified packages and classes structure

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:33 +00:00
1 parent 208c587ddf
commit f6fcd21404
34 files changed
+412 -564

No files matched your search

@@ -33,13 +33,13 @@ class MainApplication : Application() {
}
private fun checkAutoUpdate() {
val settingsRepository = container.settingsRepository
if (settingsRepository.isUpdateEnabled()) {
val timeDelta = System.currentTimeMillis() - settingsRepository.getLastUpdateTime()
val settingsRepo = container.settingsRepo
if (settingsRepo.isUpdateEnabled()) {
val timeDelta = System.currentTimeMillis() - settingsRepo.getLastUpdateTime()
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())}")
container.dataRepository.updateFromWeb()
container.databaseRepo.updateFromWeb()
}
}
}
@@ -7,23 +7,22 @@ import android.hardware.SensorManager
import android.location.LocationManager
import androidx.room.Room
import com.rtbishop.look4sat.data.DataParser
import com.rtbishop.look4sat.data.DataRepository
import com.rtbishop.look4sat.data.SatelliteRepository
import com.rtbishop.look4sat.data.SelectionRepository
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteRepository
import com.rtbishop.look4sat.domain.ISensorRepository
import com.rtbishop.look4sat.domain.ISettingsRepository
import com.rtbishop.look4sat.data.DatabaseRepo
import com.rtbishop.look4sat.data.SatelliteRepo
import com.rtbishop.look4sat.data.SelectionRepo
import com.rtbishop.look4sat.domain.IDatabaseRepo
import com.rtbishop.look4sat.domain.ISatelliteRepo
import com.rtbishop.look4sat.domain.ISelectionRepo
import com.rtbishop.look4sat.domain.ISensorsRepo
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.framework.BluetoothReporter
import com.rtbishop.look4sat.framework.NetworkReporter
import com.rtbishop.look4sat.framework.SensorRepository
import com.rtbishop.look4sat.framework.SettingsRepository
import com.rtbishop.look4sat.framework.SensorsRepo
import com.rtbishop.look4sat.framework.SettingsRepo
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 com.rtbishop.look4sat.framework.data.local.EntriesStorage
import com.rtbishop.look4sat.framework.data.local.RadiosStorage
import com.rtbishop.look4sat.framework.data.remote.FileSource
import com.rtbishop.look4sat.framework.data.remote.NetworkSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -31,52 +30,48 @@ import org.osmdroid.config.Configuration
class MainContainer(private val context: Context) {
val bluetoothReporter = provideBluetoothReporter()
val networkReporter = provideNetworkReporter()
val settingsRepository = provideSettingsRepository()
val dataRepository = provideDataRepository(settingsRepository)
val sensorRepository = provideSensorRepository()
val satelliteRepository = provideSatelliteRepository()
private val database = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb")
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
private val localStorage = LocalStorage(database.storageDao())
val settingsRepo = provideSettingsRepo()
val databaseRepo = provideDatabaseRepo(settingsRepo)
val sensorsRepo = provideSensorRepo()
val satelliteRepo = provideSatelliteRepo()
private fun provideBluetoothReporter(): BluetoothReporter {
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO))
fun provideBluetoothReporter(): BluetoothReporter {
val btManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
return BluetoothReporter(btManager, CoroutineScope(Dispatchers.IO))
}
private fun provideNetworkReporter(): NetworkReporter {
fun provideNetworkReporter(): NetworkReporter {
return NetworkReporter(CoroutineScope(Dispatchers.IO))
}
private fun provideDataRepository(settings: ISettingsRepository): IDataRepository {
val db = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb")
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
val parser = DataParser(Dispatchers.Default)
val fileSource = FileSource(context.contentResolver, Dispatchers.IO)
val entries = EntriesStorage(db.entriesDao())
val radios = RadiosStorage(db.radiosDao())
val remoteSource = NetworkSource(Dispatchers.IO)
private fun provideDatabaseRepo(settingsRepo: ISettingsRepo): IDatabaseRepo {
val dataParser = DataParser(Dispatchers.Default)
val dataSource = DataSource(context.contentResolver, Dispatchers.IO)
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
return DataRepository(parser, fileSource, entries, radios, remoteSource, scope, settings)
return DatabaseRepo(dataParser, localStorage, dataSource, scope, settingsRepo)
}
fun provideSelectionRepository(): SelectionRepository {
return SelectionRepository(Dispatchers.Default, dataRepository, settingsRepository)
fun provideSelectionRepo(): ISelectionRepo {
return SelectionRepo(Dispatchers.Default, localStorage, settingsRepo)
}
private fun provideSensorRepository(): ISensorRepository {
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
return SensorRepository(manager, sensor)
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 provideSatelliteRepository(): ISatelliteRepository {
return SatelliteRepository(Dispatchers.Default)
private fun provideSatelliteRepo(): ISatelliteRepo {
return SatelliteRepo(Dispatchers.Default, localStorage)
}
private fun provideSettingsRepository(): ISettingsRepository {
val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private fun provideSettingsRepo(): ISettingsRepo {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
Configuration.getInstance().load(context, preferences)
return SettingsRepository(manager, preferences)
return SettingsRepo(locationManager, preferences)
}
}
@@ -21,14 +21,14 @@ import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import com.rtbishop.look4sat.domain.ISensorRepository
import com.rtbishop.look4sat.domain.ISensorsRepo
import com.rtbishop.look4sat.domain.RAD2DEG
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlin.math.round
class SensorRepository(private val manager: SensorManager, private val sensor: Sensor?) :
SensorEventListener, ISensorRepository {
class SensorsRepo(private val sensorManager: SensorManager, private val sensor: Sensor?) :
SensorEventListener, ISensorsRepo {
private val rotationMatrix = FloatArray(9)
private val orientationValues = FloatArray(3)
@@ -38,10 +38,10 @@ class SensorRepository(private val manager: SensorManager, private val sensor: S
override val orientation: StateFlow<Pair<Float, Float>> = _orientation
override fun enableSensor() {
sensor?.let { manager.registerListener(this, it, 16000) }
sensor?.let { sensorManager.registerListener(this, it, 16000) }
}
override fun disableSensor() = manager.unregisterListener(this)
override fun disableSensor() = sensorManager.unregisterListener(this)
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
sensorAccuracy = accuracy
@@ -23,7 +23,7 @@ import android.location.LocationManager
import androidx.core.content.edit
import androidx.core.location.LocationManagerCompat
import androidx.core.os.CancellationSignal
import com.rtbishop.look4sat.domain.ISettingsRepository
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.GeoPos
import com.rtbishop.look4sat.utility.QthConverter
import com.rtbishop.look4sat.utility.round
@@ -31,9 +31,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.concurrent.Executors
class SettingsRepository(
class SettingsRepo(
private val manager: LocationManager, private val preferences: SharedPreferences
) : ISettingsRepository {
) : ISettingsRepo {
private val keyModes = "satModes"
private val keyCompass = "compass"
@@ -15,21 +15,25 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.framework.data.remote
package com.rtbishop.look4sat.framework.data
import android.content.ContentResolver
import android.net.Uri
import com.rtbishop.look4sat.data.IFileSource
import com.rtbishop.look4sat.data.IDataSource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.net.URL
class FileSource(
private val contentResolver: ContentResolver,
private val coroutineDispatcher: CoroutineDispatcher
) : IFileSource {
class DataSource(
private val resolver: ContentResolver, private val dispatcher: CoroutineDispatcher
) : IDataSource {
override suspend fun getDataStream(uri: String): InputStream? {
return withContext(coroutineDispatcher) { contentResolver.openInputStream(Uri.parse(uri)) }
override suspend fun getFileStream(uri: String): InputStream? {
return withContext(dispatcher) { resolver.openInputStream(Uri.parse(uri)) }
}
override suspend fun getNetworkStream(url: String): InputStream? {
return withContext(dispatcher) { URL(url).openStream() }
}
}
@@ -15,38 +15,42 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.framework.data.local
package com.rtbishop.look4sat.framework.data
import com.rtbishop.look4sat.data.IEntriesStorage
import com.rtbishop.look4sat.data.ILocalStorage
import com.rtbishop.look4sat.domain.Satellite
import com.rtbishop.look4sat.framework.data.dao.EntriesDao
import com.rtbishop.look4sat.model.SatRadio
import com.rtbishop.look4sat.framework.model.SatEntry as FrameworkEntry
import com.rtbishop.look4sat.framework.model.SatItem as FrameworkItem
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
import com.rtbishop.look4sat.model.SatEntry as DomainEntry
import com.rtbishop.look4sat.model.SatItem as DomainItem
import com.rtbishop.look4sat.model.SatRadio as DomainRadio
class EntriesStorage(private val entriesDao: EntriesDao) : IEntriesStorage {
class LocalStorage(private val storageDao: StorageDao) : ILocalStorage {
override fun getEntriesTotal() = entriesDao.getEntriesTotal()
//region # Entries region
override fun getEntriesTotal() = storageDao.getEntriesTotal()
override suspend fun getEntriesList(): List<DomainItem> {
return entriesDao.getEntriesList().toDomainItems()
return storageDao.getEntriesList().toDomainItems()
}
override suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite> {
val selectedSatellites = mutableListOf<Satellite>()
ids.chunked(999).forEach { idsPart ->
val entries = entriesDao.getEntriesWithIds(idsPart)
val entries = storageDao.getEntriesWithIds(idsPart)
selectedSatellites.addAll(entries.map { entry -> entry.data.getSatellite() })
}
return selectedSatellites
}
override suspend fun insertEntries(entries: List<DomainEntry>) {
entriesDao.insertEntries(entries.toFrameworkEntries())
storageDao.insertEntries(entries.toFrameworkEntries())
}
override suspend fun deleteEntries() = entriesDao.deleteEntries()
override suspend fun deleteEntries() = storageDao.deleteEntries()
private fun DomainEntry.toFramework() = FrameworkEntry(this.data, this.comment)
@@ -55,4 +59,36 @@ class EntriesStorage(private val entriesDao: EntriesDao) : IEntriesStorage {
private fun List<DomainEntry>.toFrameworkEntries() = this.map { entry -> entry.toFramework() }
private fun List<FrameworkItem>.toDomainItems() = this.map { item -> item.toDomain() }
//endregion
//region # Radios region
override fun getRadiosTotal() = storageDao.getRadiosTotal()
override suspend fun getRadiosWithId(id: Int): List<SatRadio> {
return storageDao.getRadiosWithId(id).toDomainRadios()
}
override suspend fun insertRadios(radios: List<SatRadio>) {
storageDao.insertRadios(radios.toFrameworkRadios())
}
override suspend fun deleteRadios() = storageDao.deleteRadios()
private fun DomainRadio.toFramework() = FrameworkRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun FrameworkRadio.toDomain() = DomainRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun List<DomainRadio>.toFrameworkRadios() = this.map { radio -> radio.toFramework() }
private fun List<FrameworkRadio>.toDomainRadios() = this.map { radio -> radio.toDomain() }
//endregion
}
@@ -21,17 +21,13 @@ import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.rtbishop.look4sat.framework.data.dao.EntriesDao
import com.rtbishop.look4sat.framework.data.dao.RadiosDao
import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.SatRadio
@Database(entities = [SatEntry::class, SatRadio::class], version = 2)
abstract class MainDatabase : RoomDatabase() {
abstract fun entriesDao(): EntriesDao
abstract fun radiosDao(): RadiosDao
abstract fun storageDao(): StorageDao
}
val MIGRATION_1_2 = object : Migration(1, 2) {
@@ -15,22 +15,23 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.framework.data.dao
package com.rtbishop.look4sat.framework.data
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 EntriesDao {
interface StorageDao {
@Query("SELECT COUNT(*) FROM entries")
fun getEntriesTotal(): Flow<Int>
// @Transaction
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
suspend fun getEntriesList(): List<SatItem>
//@Transaction
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
suspend fun getEntriesList(): List<SatItem>
@Transaction
@Query("SELECT * FROM entries WHERE catnum IN (:selectedIds)")
@@ -41,4 +42,17 @@ suspend fun getEntriesList(): List<SatItem>
@Query("DELETE FROM entries")
suspend fun deleteEntries()
@Query("SELECT COUNT(*) FROM radios")
fun getRadiosTotal(): Flow<Int>
@Transaction
@Query("SELECT * FROM radios WHERE catnum = :id AND isAlive = 1")
suspend fun getRadiosWithId(id: Int): List<SatRadio>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertRadios(radios: List<SatRadio>)
@Query("DELETE FROM radios")
suspend fun deleteRadios()
}
@@ -1,39 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.framework.data.dao
import androidx.room.*
import com.rtbishop.look4sat.framework.model.SatRadio
import kotlinx.coroutines.flow.Flow
@Dao
interface RadiosDao {
@Query("SELECT COUNT(*) FROM radios")
fun getRadiosTotal(): Flow<Int>
@Transaction
@Query("SELECT * FROM radios WHERE catnum = :id AND isAlive = 1")
suspend fun getRadiosWithId(id: Int): List<SatRadio>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertRadios(radios: List<SatRadio>)
@Query("DELETE FROM radios")
suspend fun deleteRadios()
}
@@ -1,52 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.framework.data.local
import com.rtbishop.look4sat.data.IRadiosStorage
import com.rtbishop.look4sat.framework.data.dao.RadiosDao
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
import com.rtbishop.look4sat.model.SatRadio as DomainRadio
class RadiosStorage(private val radiosDao: RadiosDao) : IRadiosStorage {
override fun getRadiosTotal() = radiosDao.getRadiosTotal()
override suspend fun getRadiosWithId(id: Int): List<DomainRadio> {
return radiosDao.getRadiosWithId(id).toDomainRadios()
}
override suspend fun insertRadios(radios: List<DomainRadio>) {
radiosDao.insertRadios(radios.toFrameworkRadios())
}
override suspend fun deleteRadios() = radiosDao.deleteRadios()
private fun DomainRadio.toFramework() = FrameworkRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun FrameworkRadio.toDomain() = DomainRadio(
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
this.mode, this.isInverted, this.catnum, this.comment
)
private fun List<DomainRadio>.toFrameworkRadios() = this.map { radio -> radio.toFramework() }
private fun List<FrameworkRadio>.toDomainRadios() = this.map { radio -> radio.toDomain() }
}
@@ -1,31 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.framework.data.remote
import com.rtbishop.look4sat.data.INetworkSource
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.net.URL
class NetworkSource(private val coroutineDispatcher: CoroutineDispatcher) : INetworkSource {
override suspend fun getDataStream(url: String): InputStream? {
return withContext(coroutineDispatcher) { URL(url).openStream() }
}
}
@@ -5,7 +5,6 @@ import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -25,8 +24,6 @@ import androidx.compose.ui.composed
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
@Composable
fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier) {
@@ -66,9 +63,3 @@ fun gotoUrl(context: Context, url: String) {
fun Modifier.onClick(onClick: () -> Unit): Modifier = composed {
clickable(remember { MutableInteractionSource() }, null) { onClick() }
}
class NoRippleInteractionSource : MutableInteractionSource {
override val interactions: Flow<Interaction> = emptyFlow()
override suspend fun emit(interaction: Interaction) {}
override fun tryEmit(interaction: Interaction) = true
}
@@ -45,8 +45,8 @@ fun EntriesScreen(navToPasses: () -> Unit) {
}
}
val unselectAll = { viewModel.updateSelection(false) }
val selectAll = { viewModel.updateSelection(true) }
val unselectAll = { viewModel.setSelection(false) }
val selectAll = { viewModel.setSelection(true) }
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TopBar(setQuery = { newQuery: String -> viewModel.setQuery(newQuery) }, saveSelection = {
viewModel.saveSelection()
@@ -58,7 +58,7 @@ fun EntriesScreen(navToPasses: () -> Unit) {
CardLoadingIndicator()
} else {
EntriesCard(uiState.itemsList) { list, value ->
viewModel.updateSelection(list, value)
viewModel.setSelection(list, value)
}
}
}
@@ -2,7 +2,7 @@ package com.rtbishop.look4sat.presentation.entries
import com.rtbishop.look4sat.model.SatItem
data class EntriesScreenState(
data class EntriesUiState(
val isLoading: Boolean,
val itemsList: List<SatItem>,
val currentType: String,
@@ -24,13 +24,13 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.data.SelectionRepository
import com.rtbishop.look4sat.domain.ISelectionRepo
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class EntriesViewModel(private val selectionRepo: SelectionRepository) : ViewModel() {
class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() {
private val defaultUiState = EntriesScreenState(
private val defaultUiState = EntriesUiState(
isLoading = true,
itemsList = emptyList(),
currentType = selectionRepo.getCurrentType(),
@@ -54,12 +54,12 @@ class EntriesViewModel(private val selectionRepo: SelectionRepository) : ViewMod
fun setQuery(query: String) = viewModelScope.launch { selectionRepo.setQuery(query) }
fun updateSelection(selectAll: Boolean) = viewModelScope.launch {
selectionRepo.updateSelection(selectAll)
fun setSelection(selectAll: Boolean) = viewModelScope.launch {
selectionRepo.setSelection(selectAll)
}
fun updateSelection(catNums: List<Int>, isSelected: Boolean) = viewModelScope.launch {
selectionRepo.updateSelection(catNums, isSelected)
fun setSelection(ids: List<Int>, isTicked: Boolean) = viewModelScope.launch {
selectionRepo.setSelection(ids, isTicked)
}
fun saveSelection() = viewModelScope.launch { selectionRepo.saveSelection() }
@@ -69,7 +69,7 @@ class EntriesViewModel(private val selectionRepo: SelectionRepository) : ViewMod
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer {
val container = (this[applicationKey] as MainApplication).container
EntriesViewModel(container.provideSelectionRepository())
EntriesViewModel(container.provideSelectionRepo())
}
}
}
@@ -65,7 +65,7 @@ private val labelRect = Rect()
fun MapScreen() {
val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory)
viewModel.selectDefaultSatellite(-1)
val stationPos = viewModel.stationPos.collectAsState(initial = null)
val stationPos = viewModel.stationPosition.collectAsState(initial = null)
val positions = viewModel.positions.collectAsState(initial = null)
val satTrack = viewModel.track.collectAsState(initial = null)
val footprint = viewModel.footprint.collectAsState(initial = null)
@@ -23,9 +23,8 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteRepository
import com.rtbishop.look4sat.domain.ISettingsRepository
import com.rtbishop.look4sat.domain.ISatelliteRepo
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.domain.Satellite
import com.rtbishop.look4sat.model.GeoPos
import com.rtbishop.look4sat.model.SatPos
@@ -46,22 +45,19 @@ import kotlin.math.max
import kotlin.math.min
class MapViewModel(
private val dataRepository: IDataRepository,
private val satelliteRepository: ISatelliteRepository,
private val settingsRepository: ISettingsRepository
private val satelliteRepo: ISatelliteRepo, private val settingsRepo: ISettingsRepo
) : ViewModel() {
private val stationPosNew = settingsRepository.stationPosition.value
private val stationPosition = GeoPos(stationPosNew.latitude, stationPosNew.longitude)
private var allPasses = satelliteRepository.getPasses()
private val stationPos = settingsRepo.stationPosition.value
private var allPasses = satelliteRepo.getPasses()
private var allSatellites = listOf<Satellite>()
private var dataUpdateJob: Job? = null
private var dataUpdateRate = 1000L
private var selectedSatellite: Satellite? = null
val stationPos = flow {
val osmLat = clipLat(stationPosition.latitude)
val osmLon = clipLon(stationPosition.longitude)
val stationPosition = flow {
val osmLat = clipLat(stationPos.latitude)
val osmLon = clipLon(stationPos.longitude)
emit(GeoPos(osmLat, osmLon))
}
@@ -92,8 +88,8 @@ class MapViewModel(
fun selectDefaultSatellite(catnum: Int) {
viewModelScope.launch {
val selectedIds = settingsRepository.satelliteSelection.value
dataRepository.getEntriesWithIds(selectedIds).also { satellites ->
val selectedIds = settingsRepo.satelliteSelection.value
satelliteRepo.getEntriesWithIds(selectedIds).also { satellites ->
if (satellites.isNotEmpty()) {
allSatellites = satellites
if (catnum == -1) {
@@ -113,12 +109,12 @@ class MapViewModel(
dataUpdateJob?.cancelAndJoin()
dataUpdateJob = launch {
val dateNow = Date()
getSatTrack(satellite, stationPosition, dateNow)
getSatTrack(satellite, stationPos, dateNow)
while (isActive) {
dateNow.time = System.currentTimeMillis()
getPositions(allSatellites, stationPosition, dateNow)
getSatFootprint(satellite, stationPosition, dateNow)
getSatData(satellite, stationPosition, dateNow)
getPositions(allSatellites, stationPos, dateNow)
getSatFootprint(satellite, stationPos, dateNow)
getSatData(satellite, stationPos, dateNow)
delay(updateFreq)
}
}
@@ -130,7 +126,7 @@ class MapViewModel(
val currentTrack = mutableListOf<GeoPos>()
val endDate = Date(date.time + (satellite.data.orbitalPeriod * 2.4 * 60000L).toLong())
var oldLongitude = 0.0
satelliteRepository.getTrack(satellite, pos, date.time, endDate.time).forEach { satPos ->
satelliteRepo.getTrack(satellite, pos, date.time, endDate.time).forEach { satPos ->
val osmLat = clipLat(satPos.latitude.toDegrees())
val osmLon = clipLon(satPos.longitude.toDegrees())
val currentPosition = GeoPos(osmLat, osmLon)
@@ -157,7 +153,7 @@ class MapViewModel(
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
val positions = mutableMapOf<Satellite, GeoPos>()
satellites.forEach { satellite ->
val satPos = satelliteRepository.getPosition(satellite, pos, date.time)
val satPos = satelliteRepo.getPosition(satellite, pos, date.time)
val osmLat = clipLat(satPos.latitude.toDegrees())
val osmLon = clipLon(satPos.longitude.toDegrees())
positions[satellite] = GeoPos(osmLat, osmLon)
@@ -166,7 +162,7 @@ class MapViewModel(
}
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
val satPos = satelliteRepository.getPosition(satellite, pos, date.time)
val satPos = satelliteRepo.getPosition(satellite, pos, date.time)
_footprint.emit(satPos)
}
@@ -184,7 +180,7 @@ class MapViewModel(
}
}
}
val satPos = satelliteRepository.getPosition(sat, pos, date.time)
val satPos = satelliteRepo.getPosition(sat, pos, date.time)
val azimuth = satPos.azimuth.toDegrees()
val elevation = satPos.elevation.toDegrees()
val osmLat = clipLat(satPos.latitude.toDegrees())
@@ -232,11 +228,7 @@ class MapViewModel(
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer {
val container = (this[applicationKey] as MainApplication).container
MapViewModel(
container.dataRepository,
container.satelliteRepository,
container.settingsRepository
)
MapViewModel(container.satelliteRepo, container.settingsRepo)
}
}
}
@@ -23,9 +23,8 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteRepository
import com.rtbishop.look4sat.domain.ISettingsRepository
import com.rtbishop.look4sat.domain.ISatelliteRepo
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.DataState
import com.rtbishop.look4sat.model.SatPass
import com.rtbishop.look4sat.utility.toTimerString
@@ -38,9 +37,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
class PassesViewModel(
private val dataRepository: IDataRepository,
private val satelliteRepository: ISatelliteRepository,
private val settingsRepository: ISettingsRepository
private val satelliteRepo: ISatelliteRepo, private val settingsRepo: ISettingsRepo
) : ViewModel() {
private val _passes = MutableStateFlow<DataState<List<SatPass>>>(DataState.Loading)
@@ -50,18 +47,18 @@ class PassesViewModel(
val passes: StateFlow<DataState<List<SatPass>>> = _passes
val timerText: StateFlow<Triple<String, String, String>?> = _timerText
fun getHoursAhead() = settingsRepository.getHoursAhead()
fun getHoursAhead() = settingsRepo.getHoursAhead()
fun getMinElevation() = settingsRepository.getMinElevation()
fun getMinElevation() = settingsRepo.getMinElevation()
init {
viewModelScope.launch {
satelliteRepository.calculatedPasses.collect { passes ->
satelliteRepo.calculatedPasses.collect { passes ->
passesProcessing?.cancelAndJoin()
passesProcessing = viewModelScope.launch {
while (isActive) {
val timeNow = System.currentTimeMillis()
val newPasses = satelliteRepository.processPasses(passes, timeNow)
val newPasses = satelliteRepo.processPasses(passes, timeNow)
if (newPasses.isNotEmpty()) {
try {
@@ -88,20 +85,15 @@ class PassesViewModel(
}
}
viewModelScope.launch {
settingsRepository.satelliteSelection.collect { calculatePasses() }
}
viewModelScope.launch {
dataRepository.updateState.collect { updateState ->
if (updateState is DataState.Success) calculatePasses()
}
settingsRepo.satelliteSelection.collect { calculatePasses() }
}
}
fun shouldUseUTC() = settingsRepository.isUtcEnabled()
fun shouldUseUTC() = settingsRepo.isUtcEnabled()
fun calculatePasses(
hoursAhead: Int = 1,
minElevation: Double = settingsRepository.getMinElevation(),
minElevation: Double = settingsRepo.getMinElevation(),
timeRef: Long = System.currentTimeMillis()
) {
viewModelScope.launch {
@@ -109,12 +101,12 @@ class PassesViewModel(
// _timerText.postValue(timerDefaultText)
passesProcessing?.cancelAndJoin()
// selection?.let { items -> settingsRepository.saveEntriesSelection(items) }
settingsRepository.setHoursAhead(hoursAhead)
settingsRepository.setMinElevation(minElevation)
val stationPos = settingsRepository.stationPosition.value
val selectedIds = settingsRepository.satelliteSelection.value
val satellites = dataRepository.getEntriesWithIds(selectedIds)
satelliteRepository.calculatePasses(
settingsRepo.setHoursAhead(hoursAhead)
settingsRepo.setMinElevation(minElevation)
val stationPos = settingsRepo.stationPosition.value
val selectedIds = settingsRepo.satelliteSelection.value
val satellites = satelliteRepo.getEntriesWithIds(selectedIds)
satelliteRepo.calculatePasses(
satellites, stationPos, timeRef, hoursAhead, minElevation
)
}
@@ -125,11 +117,7 @@ class PassesViewModel(
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer {
val container = (this[applicationKey] as MainApplication).container
PassesViewModel(
container.dataRepository,
container.satelliteRepository,
container.settingsRepository
)
PassesViewModel(container.satelliteRepo, container.settingsRepo)
}
}
}
@@ -28,10 +28,9 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteRepository
import com.rtbishop.look4sat.domain.ISensorRepository
import com.rtbishop.look4sat.domain.ISettingsRepository
import com.rtbishop.look4sat.domain.ISatelliteRepo
import com.rtbishop.look4sat.domain.ISensorsRepo
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.domain.Satellite
import com.rtbishop.look4sat.framework.BluetoothReporter
import com.rtbishop.look4sat.framework.NetworkReporter
@@ -47,26 +46,25 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
class RadarViewModel(
private val savedStateHandle: SavedStateHandle,
private val bluetoothReporter: BluetoothReporter,
private val networkReporter: NetworkReporter,
private val savedStateHandle: SavedStateHandle,
private val dataRepository: IDataRepository,
private val satelliteRepository: ISatelliteRepository,
private val sensorRepository: ISensorRepository,
private val settingsRepository: ISettingsRepository
private val satelliteRepo: ISatelliteRepo,
private val settingsRepo: ISettingsRepo,
private val sensorsRepo: ISensorsRepo
) : ViewModel() {
private val stationPos = settingsRepository.stationPosition.value
private val stationPos = settingsRepo.stationPosition.value
private val magDeclination = getMagDeclination(stationPos)
val transmitters = mutableStateOf<List<SatRadio>>(emptyList())
val radarData = mutableStateOf<RadarData?>(null)
val orientation = mutableStateOf(sensorRepository.orientation.value)
val orientation = mutableStateOf(sensorsRepo.orientation.value)
init {
if (settingsRepository.isSensorEnabled()) {
if (settingsRepo.isSensorEnabled()) {
viewModelScope.launch {
sensorRepository.enableSensor()
sensorRepository.orientation.collect { data ->
sensorsRepo.enableSensor()
sensorsRepo.orientation.collect { data ->
orientation.value = Pair(data.first + magDeclination, data.second)
}
}
@@ -74,24 +72,24 @@ class RadarViewModel(
}
override fun onCleared() {
sensorRepository.disableSensor()
sensorsRepo.disableSensor()
super.onCleared()
}
fun getPass() = flow {
val catNum = savedStateHandle.get<Int>("catNum") ?: 0
val aosTime = savedStateHandle.get<Long>("aosTime") ?: 0L
satelliteRepository.calculatedPasses.collect { passes ->
satelliteRepo.calculatedPasses.collect { passes ->
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
val currentPass = pass ?: passes.firstOrNull()
currentPass?.let { satPass ->
emit(satPass)
val transmitters = dataRepository.getRadiosWithId(satPass.catNum)
val transmitters = satelliteRepo.getRadiosWithId(satPass.catNum)
viewModelScope.launch {
while (isActive) {
val timeNow = System.currentTimeMillis()
val satPos =
satelliteRepository.getPosition(satPass.satellite, stationPos, timeNow)
satelliteRepo.getPosition(satPass.satellite, stationPos, timeNow)
sendPassData(satPass, satPos, satPass.satellite)
sendPassDataBT(satPos)
processRadios(transmitters, satPass.satellite, timeNow)
@@ -102,27 +100,27 @@ class RadarViewModel(
}
}
fun getUseCompass(): Boolean = settingsRepository.isSensorEnabled()
fun getUseCompass(): Boolean = settingsRepo.isSensorEnabled()
fun getShowSweep(): Boolean = settingsRepository.isSweepEnabled()
fun getShowSweep(): Boolean = settingsRepo.isSweepEnabled()
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
val latitude = geoPos.latitude.toFloat()
val longitude = geoPos.longitude.toFloat()
return GeomagneticField(latitude, longitude, 0f, time).declination
return GeomagneticField(latitude, longitude, geoPos.altitude.toFloat(), time).declination
}
private fun sendPassData(satPass: SatPass, satPos: SatPos, satellite: Satellite) {
viewModelScope.launch {
var track: List<SatPos> = emptyList()
if (!satPass.isDeepSpace) {
track = satelliteRepository.getTrack(
track = satelliteRepo.getTrack(
satellite, stationPos, satPass.aosTime, satPass.losTime
)
}
if (settingsRepository.getRotatorEnabled()) {
val server = settingsRepository.getRotatorServer()
val port = settingsRepository.getRotatorPort().toInt()
if (settingsRepo.getRotatorEnabled()) {
val server = settingsRepo.getRotatorServer()
val port = settingsRepo.getRotatorPort().toInt()
val azimuth = satPos.azimuth.toDegrees().round(1)
val elevation = satPos.elevation.toDegrees().round(1)
networkReporter.reportRotation(server, port, azimuth, elevation)
@@ -133,10 +131,10 @@ class RadarViewModel(
private fun sendPassDataBT(satPos: SatPos) {
viewModelScope.launch {
if (settingsRepository.getBTEnabled()) {
val btDevice = settingsRepository.getBTDeviceAddr()
if (settingsRepo.getBTEnabled()) {
val btDevice = settingsRepo.getBTDeviceAddr()
if (bluetoothReporter.isConnected()) {
val format = settingsRepository.getBTFormat()
val format = settingsRepo.getBTFormat()
val azimuth = satPos.azimuth.toDegrees().round(0).toInt()
val elevation = satPos.elevation.toDegrees().round(0).toInt()
bluetoothReporter.reportRotation(format, azimuth, elevation)
@@ -151,7 +149,7 @@ class RadarViewModel(
private fun processRadios(radios: List<SatRadio>, satellite: Satellite, time: Long) {
viewModelScope.launch {
delay(125)
val list = satelliteRepository.processRadios(satellite, stationPos, radios, time)
val list = satelliteRepo.processRadios(satellite, stationPos, radios, time)
transmitters.value = list
}
}
@@ -160,16 +158,14 @@ class RadarViewModel(
val Factory: ViewModelProvider.Factory = viewModelFactory {
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer {
val savedStateHandle = createSavedStateHandle()
val container = (this[applicationKey] as MainApplication).container
RadarViewModel(
container.bluetoothReporter,
container.networkReporter,
savedStateHandle,
container.dataRepository,
container.satelliteRepository,
container.sensorRepository,
container.settingsRepository
createSavedStateHandle(),
container.provideBluetoothReporter(),
container.provideNetworkReporter(),
container.satelliteRepo,
container.settingsRepo,
container.sensorsRepo
)
}
}
@@ -25,18 +25,18 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISettingsRepository
import com.rtbishop.look4sat.domain.IDatabaseRepo
import com.rtbishop.look4sat.domain.ISettingsRepo
import com.rtbishop.look4sat.model.DataState
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
class SettingsViewModel(
private val dataRepository: IDataRepository, private val settingsRepository: ISettingsRepository
private val databaseRepo: IDatabaseRepo, private val settingsRepo: ISettingsRepo
) : ViewModel() {
private val defaultLocationSettings = LocationSettings(false,
settingsRepository.stationPosition.value,
settingsRepo.stationPosition.value,
0,
{ setGpsPosition() },
{ latitude, longitude -> setGeoPosition(latitude, longitude) },
@@ -45,18 +45,18 @@ class SettingsViewModel(
val locationSettings = mutableStateOf(defaultLocationSettings)
private val defaultDataSettings = DataSettings(false,
settingsRepository.getLastUpdateTime(),
settingsRepo.getLastUpdateTime(),
0,
0,
{ dataRepository.updateFromWeb() },
{ dataRepository.updateFromFile(it) },
{ dataRepository.clearAllData() })
{ databaseRepo.updateFromWeb() },
{ databaseRepo.updateFromFile(it) },
{ databaseRepo.clearAllData() })
val dataSettings = mutableStateOf(defaultDataSettings)
private val defaultOtherSettings = OtherSettings(settingsRepository.isUtcEnabled(),
settingsRepository.isUpdateEnabled(),
settingsRepository.isSweepEnabled(),
settingsRepository.isSensorEnabled(),
private val defaultOtherSettings = OtherSettings(settingsRepo.isUtcEnabled(),
settingsRepo.isUpdateEnabled(),
settingsRepo.isSweepEnabled(),
settingsRepo.isSensorEnabled(),
{ setUtc(it) },
{ setUpdate(it) },
{ setSweep(it) },
@@ -65,23 +65,23 @@ class SettingsViewModel(
init {
viewModelScope.launch {
settingsRepository.stationPosition.collect { stationPos ->
settingsRepo.stationPosition.collect { stationPos ->
locationSettings.value = locationSettings.value.copy(
isUpdating = false, stationPos = stationPos
)
}
}
viewModelScope.launch {
dataRepository.updateState.collect {
databaseRepo.updateState.collect {
val isUpdating = it is DataState.Loading
dataSettings.value = dataSettings.value.copy(
isUpdating = isUpdating, lastUpdated = settingsRepository.getLastUpdateTime()
isUpdating = isUpdating, lastUpdated = settingsRepo.getLastUpdateTime()
)
}
}
viewModelScope.launch {
combine(
dataRepository.getEntriesTotal(), dataRepository.getRadiosTotal()
databaseRepo.getEntriesTotal(), databaseRepo.getRadiosTotal()
) { sats, radios ->
dataSettings.value = dataSettings.value.copy(satsTotal = sats, radiosTotal = radios)
}.collect {}
@@ -89,7 +89,7 @@ class SettingsViewModel(
}
private fun setGpsPosition() {
if (settingsRepository.setGpsPosition()) {
if (settingsRepo.setGpsPosition()) {
val messageResId = R.string.location_success
locationSettings.value =
locationSettings.value.copy(isUpdating = true, messageResId = messageResId)
@@ -100,7 +100,7 @@ class SettingsViewModel(
}
private fun setGeoPosition(latitude: Double, longitude: Double) {
if (settingsRepository.setGeoPosition(latitude, longitude)) {
if (settingsRepo.setGeoPosition(latitude, longitude)) {
val messageResId = R.string.location_success
locationSettings.value = locationSettings.value.copy(messageResId = messageResId)
} else {
@@ -110,7 +110,7 @@ class SettingsViewModel(
}
private fun setQthPosition(locator: String) {
if (settingsRepository.setQthPosition(locator)) {
if (settingsRepo.setQthPosition(locator)) {
val messageResId = R.string.location_success
locationSettings.value = locationSettings.value.copy(messageResId = messageResId)
} else {
@@ -124,22 +124,22 @@ class SettingsViewModel(
}
private fun setUtc(value: Boolean) {
settingsRepository.setUtcState(value)
settingsRepo.setUtcState(value)
otherSettings.value = otherSettings.value.copy(getUtc = value)
}
private fun setUpdate(value: Boolean) {
settingsRepository.setUpdateState(value)
settingsRepo.setUpdateState(value)
otherSettings.value = otherSettings.value.copy(getUpdate = value)
}
private fun setSweep(value: Boolean) {
settingsRepository.setSweepState(value)
settingsRepo.setSweepState(value)
otherSettings.value = otherSettings.value.copy(getSweep = value)
}
private fun setSensor(value: Boolean) {
settingsRepository.setSensorState(value)
settingsRepo.setSensorState(value)
otherSettings.value = otherSettings.value.copy(getSensor = value)
}
@@ -163,7 +163,7 @@ class SettingsViewModel(
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer {
val container = (this[applicationKey] as MainApplication).container
SettingsViewModel(container.dataRepository, container.settingsRepository)
SettingsViewModel(container.databaseRepo, container.settingsRepo)
}
}
}