Further code refactoring and cleanup

This commit is contained in:
Arty Bishop committed 2021-10-27 21:47:02 +01:00
1 parent 253321f3c2
commit bf72cd2061
20 files changed
+173 -212

No files matched your search

@@ -17,70 +17,39 @@
*/ */
package com.rtbishop.look4sat.framework package com.rtbishop.look4sat.framework
import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.SatItem
import com.rtbishop.look4sat.framework.model.Transmitter
import com.rtbishop.look4sat.domain.model.SatEntry as DomainEntry import com.rtbishop.look4sat.domain.model.SatEntry as DomainEntry
import com.rtbishop.look4sat.domain.model.SatItem as DomainItem import com.rtbishop.look4sat.domain.model.SatItem as DomainItem
import com.rtbishop.look4sat.domain.model.Transmitter as DomainTrans import com.rtbishop.look4sat.domain.model.Transmitter as DomainTransmitter
import com.rtbishop.look4sat.framework.model.SatEntry as FrameworkEntry
import com.rtbishop.look4sat.framework.model.SatItem as FrameworkItem
import com.rtbishop.look4sat.framework.model.Transmitter as FrameworkTransmitter
object DataMapper { fun DomainEntry.toFramework() = FrameworkEntry(this.tle, this.isSelected)
// Presentation to Domain fun DomainItem.toFramework() = FrameworkItem(this.catnum, this.name, this.isSelected, this.modes)
fun satEntryToDomainEntry(entry: SatEntry): DomainEntry { fun DomainTransmitter.toFramework() = FrameworkTransmitter(
return DomainEntry(entry.tle, entry.isSelected) this.uuid, this.info, this.isAlive, this.downlink,
} this.uplink, this.mode, this.isInverted, this.catnum
fun satEntriesToDomainEntries(entries: List<SatEntry>): List<DomainEntry> {
return entries.map { entry -> satEntryToDomainEntry(entry) }
}
fun satItemToDomainItem(item: SatItem): DomainItem {
return DomainItem(item.catnum, item.name, item.isSelected, item.modes)
}
fun satItemsToDomainItems(items: List<SatItem>): List<DomainItem> {
return items.map { item -> satItemToDomainItem(item) }
}
fun satTransToDomainTrans(transmitter: Transmitter): DomainTrans {
return DomainTrans(
transmitter.uuid, transmitter.info, transmitter.isAlive, transmitter.downlink,
transmitter.uplink, transmitter.mode, transmitter.isInverted, transmitter.catnum
) )
}
fun satTransListToDomainTransList(transmitters: List<Transmitter>): List<DomainTrans> { fun FrameworkEntry.toDomain() = DomainEntry(this.tle, this.isSelected)
return transmitters.map { transmitter -> satTransToDomainTrans(transmitter) }
}
// Domain to Presentation fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.isSelected, this.modes)
fun domainEntryToSatEntry(entry: DomainEntry): SatEntry { fun FrameworkTransmitter.toDomain() = DomainTransmitter(
return SatEntry(entry.tle, entry.isSelected) this.uuid, this.info, this.isAlive, this.downlink,
} this.uplink, this.mode, this.isInverted, this.catnum
fun domainEntriesToSatEntries(entries: List<DomainEntry>): List<SatEntry> {
return entries.map { entry -> domainEntryToSatEntry(entry) }
}
fun domainItemToSatItem(item: DomainItem): SatItem {
return SatItem(item.catnum, item.name, item.isSelected, item.modes)
}
fun domainItemsToSatItems(items: List<DomainItem>): List<SatItem> {
return items.map { item -> domainItemToSatItem(item) }
}
fun domainTransToSatTrans(transmitter: DomainTrans): Transmitter {
return Transmitter(
transmitter.uuid, transmitter.info, transmitter.isAlive, transmitter.downlink,
transmitter.uplink, transmitter.mode, transmitter.isInverted, transmitter.catnum
) )
}
fun domainTransListToSatTransList(transmitters: List<DomainTrans>): List<Transmitter> { fun List<DomainEntry>.toFrameworkEntries() = this.map { it.toFramework() }
return transmitters.map { transmitter -> domainTransToSatTrans(transmitter) }
} fun List<DomainItem>.toFrameworkItems() = this.map { it.toFramework() }
}
fun List<DomainTransmitter>.toFramework() = this.map { it.toFramework() }
fun List<FrameworkEntry>.toDomainEntries() = this.map { it.toDomain() }
fun List<FrameworkItem>.toDomainItems() = this.map { it.toDomain() }
fun List<FrameworkTransmitter>.toDomain() = this.map { it.toDomain() }
@@ -27,38 +27,38 @@ interface EntriesDao {
@Transaction @Transaction
@Query("SELECT catnum, name, isSelected FROM entries ORDER BY name ASC") @Query("SELECT catnum, name, isSelected FROM entries ORDER BY name ASC")
fun getSatItems(): Flow<List<SatItem>> fun getSatelliteItems(): Flow<List<SatItem>>
@Query("SELECT * FROM entries WHERE isSelected = 1") @Query("SELECT * FROM entries WHERE isSelected = 1")
suspend fun getSelectedSatellites(): List<SatEntry> suspend fun getSelectedSatellites(): List<SatEntry>
@Transaction @Transaction
suspend fun updateEntries(entries: List<SatEntry>) { suspend fun updateEntries(entries: List<SatEntry>) {
val entriesSelection = getEntriesSelection() val savedSelection = getSelection()
insertEntries(entries) insertEntries(entries)
restoreEntriesSelection(entriesSelection, true) restoreSelection(savedSelection, true)
} }
@Query("SELECT catnum FROM entries WHERE isSelected = 1") @Query("SELECT catnum FROM entries WHERE isSelected = 1")
suspend fun getEntriesSelection(): List<Int> suspend fun getSelection(): List<Int>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertEntries(entries: List<SatEntry>) suspend fun insertEntries(entries: List<SatEntry>)
@Transaction @Transaction
suspend fun updateEntriesSelection(catnums: List<Int>, isSelected: Boolean) { suspend fun restoreSelection(catnums: List<Int>, isSelected: Boolean) {
catnums.forEach { catnum -> updateEntrySelection(catnum, isSelected) }
}
@Transaction
suspend fun restoreEntriesSelection(catnums: List<Int>, isSelected: Boolean) {
clearEntriesSelection() clearEntriesSelection()
catnums.forEach { catnum -> updateEntrySelection(catnum, isSelected) } updateSelection(catnums, isSelected)
} }
@Query("UPDATE entries SET isSelected = 0") @Query("UPDATE entries SET isSelected = 0")
suspend fun clearEntriesSelection() suspend fun clearEntriesSelection()
@Transaction
suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean) {
catnums.forEach { catnum -> updateEntrySelection(catnum, isSelected) }
}
@Query("UPDATE entries SET isSelected = :isSelected WHERE catnum = :catnum") @Query("UPDATE entries SET isSelected = :isSelected WHERE catnum = :catnum")
suspend fun updateEntrySelection(catnum: Int, isSelected: Boolean) suspend fun updateEntrySelection(catnum: Int, isSelected: Boolean)
} }
@@ -19,12 +19,9 @@ package com.rtbishop.look4sat.framework.local
import com.rtbishop.look4sat.data.LocalDataSource import com.rtbishop.look4sat.data.LocalDataSource
import com.rtbishop.look4sat.domain.model.SatEntry import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.domain.model.Transmitter import com.rtbishop.look4sat.domain.model.Transmitter
import com.rtbishop.look4sat.framework.DataMapper import com.rtbishop.look4sat.framework.*
import com.rtbishop.look4sat.framework.model.Source import com.rtbishop.look4sat.framework.model.Source
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
class LocalSource( class LocalSource(
@@ -33,45 +30,31 @@ class LocalSource(
private val transmittersDao: TransmittersDao private val transmittersDao: TransmittersDao
) : LocalDataSource { ) : LocalDataSource {
// Entries override fun getSatelliteItems() = entriesDao.getSatelliteItems().map { it.toDomainItems() }
override fun getEntriesWithModes(): Flow<List<SatItem>> {
return entriesDao.getSatItems().map { satItems -> override suspend fun getSelectedSatellites() = entriesDao.getSelectedSatellites().map { entry ->
DataMapper.satItemsToDomainItems(satItems) entry.tle.createSat()
}
} }
override suspend fun getSelectedSatellites(): List<Satellite> { override suspend fun getTransmitters(catnum: Int): List<Transmitter> {
return entriesDao.getSelectedSatellites().map { it.tle.createSat() } return transmittersDao.getTransmitters(catnum).toDomain()
} }
override suspend fun getWebSources() = sourcesDao.getSources()
override suspend fun updateEntries(entries: List<SatEntry>) { override suspend fun updateEntries(entries: List<SatEntry>) {
val satEntries = entries.map { entry -> DataMapper.domainEntryToSatEntry(entry) } entriesDao.updateEntries(entries.toFrameworkEntries())
entriesDao.updateEntries(satEntries)
} }
override suspend fun updateEntriesSelection(catnums: List<Int>, isSelected: Boolean) { override suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean) {
entriesDao.updateEntriesSelection(catnums, isSelected) entriesDao.updateSelection(catnums, isSelected)
}
// Sources
override suspend fun getSources(): List<String> {
return sourcesDao.getSources()
}
override suspend fun updateSources(sources: List<String>) {
sourcesDao.deleteSources()
sourcesDao.updateSources(sources.map { Source(it) })
}
// Transmitters
override fun getTransmitters(catnum: Int): Flow<List<Transmitter>> {
return transmittersDao.getTransmitters(catnum).map { satTransList ->
DataMapper.satTransListToDomainTransList(satTransList)
}
} }
override suspend fun updateTransmitters(transmitters: List<Transmitter>) { override suspend fun updateTransmitters(transmitters: List<Transmitter>) {
val transList = DataMapper.domainTransListToSatTransList(transmitters) transmittersDao.updateTransmitters(transmitters.toFramework())
transmittersDao.updateTransmitters(transList) }
override suspend fun updateWebSources(sources: List<String>) {
sourcesDao.updateSources(sources.map { Source(it) })
} }
} }
@@ -26,7 +26,7 @@ import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.Transmitter import com.rtbishop.look4sat.framework.model.Transmitter
@Database( @Database(
entities = [SatEntry::class, Transmitter::class, Source::class], entities = [SatEntry::class, Source::class, Transmitter::class],
version = 4, version = 4,
exportSchema = true exportSchema = true
) )
@@ -1,8 +1,24 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2021 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.local package com.rtbishop.look4sat.framework.local
import androidx.room.* import androidx.room.*
import com.rtbishop.look4sat.framework.model.Source import com.rtbishop.look4sat.framework.model.Source
import com.rtbishop.look4sat.framework.model.Transmitter
@Dao @Dao
interface SourcesDao { interface SourcesDao {
@@ -1,14 +1,30 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2021 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.local package com.rtbishop.look4sat.framework.local
import androidx.room.* import androidx.room.*
import com.rtbishop.look4sat.framework.model.Transmitter import com.rtbishop.look4sat.framework.model.Transmitter
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface TransmittersDao { interface TransmittersDao {
@Query("SELECT * FROM transmitters WHERE catnum = :catnum") @Query("SELECT * FROM transmitters WHERE catnum = :catnum")
fun getTransmitters(catnum: Int): Flow<List<Transmitter>> suspend fun getTransmitters(catnum: Int): List<Transmitter>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun updateTransmitters(transmitters: List<Transmitter>) suspend fun updateTransmitters(transmitters: List<Transmitter>)
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.framework.remote
import com.rtbishop.look4sat.data.RemoteDataSource import com.rtbishop.look4sat.data.RemoteDataSource
import com.rtbishop.look4sat.domain.model.Transmitter import com.rtbishop.look4sat.domain.model.Transmitter
import com.rtbishop.look4sat.framework.DataMapper import com.rtbishop.look4sat.framework.toDomain
import java.io.InputStream import java.io.InputStream
class RemoteSource(private val satelliteApi: SatelliteApi) : RemoteDataSource { class RemoteSource(private val satelliteApi: SatelliteApi) : RemoteDataSource {
@@ -29,6 +29,6 @@ class RemoteSource(private val satelliteApi: SatelliteApi) : RemoteDataSource {
} }
override suspend fun fetchTransmitters(): List<Transmitter> { override suspend fun fetchTransmitters(): List<Transmitter> {
return DataMapper.satTransListToDomainTransList(satelliteApi.fetchTransmitters()) return satelliteApi.fetchTransmitters().toDomain()
} }
} }
@@ -21,9 +21,9 @@ import android.content.Context
import androidx.room.Room import androidx.room.Room
import com.rtbishop.look4sat.data.LocalDataSource import com.rtbishop.look4sat.data.LocalDataSource
import com.rtbishop.look4sat.data.RemoteDataSource import com.rtbishop.look4sat.data.RemoteDataSource
import com.rtbishop.look4sat.data.DataRepository import com.rtbishop.look4sat.data.DefaultRepository
import com.rtbishop.look4sat.framework.remote.SatelliteApi import com.rtbishop.look4sat.framework.remote.SatelliteApi
import com.rtbishop.look4sat.domain.SatelliteRepo import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.domain.DataReporter import com.rtbishop.look4sat.domain.DataReporter
import com.rtbishop.look4sat.domain.predict.Predictor import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.framework.remote.RemoteSource import com.rtbishop.look4sat.framework.remote.RemoteSource
@@ -65,8 +65,8 @@ object CoreModule {
localSource: LocalDataSource, localSource: LocalDataSource,
remoteSource: RemoteDataSource, remoteSource: RemoteDataSource,
@IoDispatcher dispatcher: CoroutineDispatcher @IoDispatcher dispatcher: CoroutineDispatcher
): SatelliteRepo { ): DataRepository {
return DataRepository(localSource, remoteSource, dispatcher) return DefaultRepository(localSource, remoteSource, dispatcher)
} }
@Provides @Provides
@@ -29,7 +29,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
import com.rtbishop.look4sat.domain.model.SatItem import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.DataState import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.presentation.ItemDivider import com.rtbishop.look4sat.presentation.ItemDivider
import com.rtbishop.look4sat.presentation.getNavResult import com.rtbishop.look4sat.presentation.getNavResult
import com.rtbishop.look4sat.presentation.navigateSafe import com.rtbishop.look4sat.presentation.navigateSafe
@@ -42,7 +42,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
private val viewModel: EntriesViewModel by viewModels() private val viewModel: EntriesViewModel by viewModels()
private val contentContract = ActivityResultContracts.GetContent() private val contentContract = ActivityResultContracts.GetContent()
private val filePicker = registerForActivityResult(contentContract) { uri -> private val filePicker = registerForActivityResult(contentContract) { uri ->
uri?.let { viewModel.updateEntriesFromFile(uri) } uri?.let { viewModel.updateDataFromFile(uri) }
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -51,11 +51,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
} }
private fun setupComponents(view: View) { private fun setupComponents(view: View) {
val entriesAdapter = EntriesAdapter(object : EntriesAdapter.EntriesClickListener { val entriesAdapter = EntriesAdapter(viewModel)
override fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
viewModel.updateSelection(catNums, isSelected)
}
})
val binding = FragmentEntriesBinding.bind(view).apply { val binding = FragmentEntriesBinding.bind(view).apply {
entriesRecycler.apply { entriesRecycler.apply {
setHasFixedSize(true) setHasFixedSize(true)
@@ -76,7 +72,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
handleSatData(satData, binding, entriesAdapter) handleSatData(satData, binding, entriesAdapter)
}) })
getNavResult<List<String>>(R.id.nav_entries, "sources") { sources -> getNavResult<List<String>>(R.id.nav_entries, "sources") { sources ->
viewModel.updateEntriesFromWeb(sources) viewModel.updateDataFromWeb(sources)
} }
} }
@@ -22,8 +22,8 @@ import android.net.Uri
import android.widget.SearchView import android.widget.SearchView
import androidx.lifecycle.* import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.model.SatItem import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.DataState import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.SatelliteRepo import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.framework.PreferencesSource import com.rtbishop.look4sat.framework.PreferencesSource
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
@@ -37,8 +37,8 @@ import javax.inject.Inject
class EntriesViewModel @Inject constructor( class EntriesViewModel @Inject constructor(
private val preferences: PreferencesSource, private val preferences: PreferencesSource,
private val resolver: ContentResolver, private val resolver: ContentResolver,
private val satelliteRepo: SatelliteRepo private val dataRepository: DataRepository
) : ViewModel(), SearchView.OnQueryTextListener { ) : ViewModel(), SearchView.OnQueryTextListener, EntriesAdapter.EntriesClickListener {
private val coroutineHandler = CoroutineExceptionHandler { _, throwable -> private val coroutineHandler = CoroutineExceptionHandler { _, throwable ->
Timber.d(throwable) Timber.d(throwable)
@@ -47,7 +47,7 @@ class EntriesViewModel @Inject constructor(
private val transModes = MutableLiveData(preferences.loadModesSelection()) private val transModes = MutableLiveData(preferences.loadModesSelection())
private val currentQuery = MutableLiveData(String()) private val currentQuery = MutableLiveData(String())
private val itemsWithModes = transModes.switchMap { modes -> private val itemsWithModes = transModes.switchMap { modes ->
liveData { satelliteRepo.getSatItems().collect { emit(filterByModes(it, modes)) } } liveData { dataRepository.getSatelliteItems().collect { emit(filterByModes(it, modes)) } }
} }
private val itemsWithQuery = currentQuery.switchMap { query -> private val itemsWithQuery = currentQuery.switchMap { query ->
itemsWithModes.map { items -> DataState.Success(filterByQuery(items, query)) } itemsWithModes.map { items -> DataState.Success(filterByQuery(items, query)) }
@@ -58,21 +58,21 @@ class EntriesViewModel @Inject constructor(
private var shouldSelectAll = true private var shouldSelectAll = true
val satData: LiveData<DataState<List<SatItem>>> = _satData val satData: LiveData<DataState<List<SatItem>>> = _satData
fun updateEntriesFromFile(uri: Uri) { fun updateDataFromFile(uri: Uri) {
viewModelScope.launch(coroutineHandler) { viewModelScope.launch(coroutineHandler) {
_satData.value = DataState.Loading _satData.value = DataState.Loading
runCatching { runCatching {
resolver.openInputStream(uri)?.use { stream -> resolver.openInputStream(uri)?.use { stream ->
satelliteRepo.updateEntriesFromFile(stream) dataRepository.updateDataFromFile(stream)
} }
}.onFailure { _satData.value = DataState.Error(it) } }.onFailure { _satData.value = DataState.Error(it) }
} }
} }
fun updateEntriesFromWeb(sources: List<String>) { fun updateDataFromWeb(sources: List<String>) {
viewModelScope.launch(coroutineHandler) { viewModelScope.launch(coroutineHandler) {
_satData.value = DataState.Loading _satData.value = DataState.Loading
satelliteRepo.updateEntriesFromWeb(sources) dataRepository.updateDataFromWeb(sources)
} }
} }
@@ -102,8 +102,8 @@ class EntriesViewModel @Inject constructor(
return true return true
} }
fun updateSelection(catNums: List<Int>, isSelected: Boolean) { override fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
viewModelScope.launch { satelliteRepo.updateEntriesSelection(catNums, isSelected) } viewModelScope.launch { dataRepository.updateSelection(catNums, isSelected) }
} }
private fun filterByModes(items: List<SatItem>, modes: List<String>): List<SatItem> { private fun filterByModes(items: List<SatItem>, modes: List<String>): List<SatItem> {
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.presentation.mapScreen
import androidx.lifecycle.* import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.predict.Predictor import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.domain.SatelliteRepo import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.framework.PreferencesSource import com.rtbishop.look4sat.framework.PreferencesSource
import com.rtbishop.look4sat.domain.predict.GeoPos import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.QthConverter import com.rtbishop.look4sat.domain.QthConverter
@@ -33,7 +33,7 @@ import kotlin.math.min
@HiltViewModel @HiltViewModel
class MapViewModel @Inject constructor( class MapViewModel @Inject constructor(
private val satelliteRepo: SatelliteRepo, private val dataRepository: DataRepository,
private val predictor: Predictor, private val predictor: Predictor,
private val preferences: PreferencesSource, private val preferences: PreferencesSource,
) : ViewModel() { ) : ViewModel() {
@@ -63,7 +63,7 @@ class MapViewModel @Inject constructor(
init { init {
viewModelScope.launch { viewModelScope.launch {
satelliteRepo.getSelectedSatellites().also { selectedSatellites -> dataRepository.getSelectedSatellites().also { selectedSatellites ->
if (selectedSatellites.isNotEmpty()) { if (selectedSatellites.isNotEmpty()) {
allSatList = selectedSatellites allSatList = selectedSatellites
selectSatellite(selectedSatellites.first()) selectSatellite(selectedSatellites.first())
@@ -30,7 +30,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentPassesBinding import com.rtbishop.look4sat.databinding.FragmentPassesBinding
import com.rtbishop.look4sat.domain.predict.SatPass import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.DataState import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.presentation.ItemDivider import com.rtbishop.look4sat.presentation.ItemDivider
import com.rtbishop.look4sat.presentation.navigateSafe import com.rtbishop.look4sat.presentation.navigateSafe
import com.rtbishop.look4sat.presentation.toTimerString import com.rtbishop.look4sat.presentation.toTimerString
@@ -23,8 +23,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.domain.predict.Predictor import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.domain.predict.SatPass import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.DataState import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.SatelliteRepo import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.framework.PreferencesSource import com.rtbishop.look4sat.framework.PreferencesSource
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -34,7 +34,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class PassesViewModel @Inject constructor( class PassesViewModel @Inject constructor(
private val satelliteRepo: SatelliteRepo, private val dataRepository: DataRepository,
private val predictor: Predictor, private val predictor: Predictor,
private val preferences: PreferencesSource private val preferences: PreferencesSource
) : ViewModel() { ) : ViewModel() {
@@ -50,7 +50,7 @@ class PassesViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
_passes.postValue(DataState.Loading) _passes.postValue(DataState.Loading)
val dateNow = Date() val dateNow = Date()
val satellites = satelliteRepo.getSelectedSatellites() val satellites = dataRepository.getSelectedSatellites()
val stationPos = preferences.loadStationPosition() val stationPos = preferences.loadStationPosition()
val hoursAhead = preferences.getHoursAhead() val hoursAhead = preferences.getHoursAhead()
val minElev = preferences.getMinElevation() val minElev = preferences.getMinElevation()
@@ -71,15 +71,13 @@ class PassesViewModel @Inject constructor(
preferences.updatePositionFromGPS() preferences.updatePositionFromGPS()
viewModelScope.launch { viewModelScope.launch {
_passes.postValue(DataState.Loading) _passes.postValue(DataState.Loading)
val defaultCatNums = listOf(43700, 25544, 25338, 28654, 33591, 40069, 27607, 24278) val satellites = dataRepository.getSelectedSatellites()
val dateNow = Date()
val satellites = satelliteRepo.getSelectedSatellites()
val stationPos = preferences.loadStationPosition() val stationPos = preferences.loadStationPosition()
val hoursAhead = preferences.getHoursAhead() val hoursAhead = preferences.getHoursAhead()
val minElev = preferences.getMinElevation() val minElev = preferences.getMinElevation()
satelliteRepo.updateEntriesFromWeb(satelliteRepo.getDefaultSources()) dataRepository.updateDataFromWeb()
satelliteRepo.updateEntriesSelection(defaultCatNums, true) dataRepository.updateSelection(isSelected = true)
predictor.forceCalculation(satellites, stationPos, dateNow, hoursAhead, minElev) predictor.forceCalculation(satellites, stationPos, Date(), hoursAhead, minElev)
preferences.setSetupDone() preferences.setSetupDone()
_isFirstLaunchDone.value = true _isFirstLaunchDone.value = true
} }
@@ -90,7 +88,7 @@ class PassesViewModel @Inject constructor(
_passes.postValue(DataState.Loading) _passes.postValue(DataState.Loading)
passesProcessing?.cancelAndJoin() passesProcessing?.cancelAndJoin()
val dateNow = Date() val dateNow = Date()
val satellites = satelliteRepo.getSelectedSatellites() val satellites = dataRepository.getSelectedSatellites()
val stationPos = preferences.loadStationPosition() val stationPos = preferences.loadStationPosition()
val hoursAhead = preferences.getHoursAhead() val hoursAhead = preferences.getHoursAhead()
val minElev = preferences.getMinElevation() val minElev = preferences.getMinElevation()
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.presentation.radarScreen
import androidx.lifecycle.* import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.DataReporter import com.rtbishop.look4sat.domain.DataReporter
import com.rtbishop.look4sat.domain.SatelliteRepo import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.domain.predict.Predictor import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.domain.predict.SatPass import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.predict.SatPos import com.rtbishop.look4sat.domain.predict.SatPos
@@ -40,7 +40,7 @@ class RadarViewModel @Inject constructor(
private val orientationSource: OrientationSource, private val orientationSource: OrientationSource,
private val preferences: PreferencesSource, private val preferences: PreferencesSource,
private val predictor: Predictor, private val predictor: Predictor,
private val satelliteRepo: SatelliteRepo, private val dataRepository: DataRepository,
private val dataReporter: DataReporter private val dataReporter: DataReporter
) : ViewModel(), OrientationSource.OrientationListener { ) : ViewModel(), OrientationSource.OrientationListener {
@@ -100,10 +100,10 @@ class RadarViewModel @Inject constructor(
private fun processTransmitters(satPass: SatPass) { private fun processTransmitters(satPass: SatPass) {
viewModelScope.launch { viewModelScope.launch {
satelliteRepo.getTransmitters(satPass.catNum).collect { transList -> val transmitters = dataRepository.getTransmitters(satPass.catNum)
while (isActive) { while (isActive) {
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date()) val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date())
val copiedList = transList.map { it.copy() } val copiedList = transmitters.map { it.copy() }
copiedList.forEach { transmitter -> copiedList.forEach { transmitter ->
transmitter.downlink?.let { transmitter.downlink?.let {
transmitter.downlink = satPos.getDownlinkFreq(it) transmitter.downlink = satPos.getDownlinkFreq(it)
@@ -118,4 +118,3 @@ class RadarViewModel @Inject constructor(
} }
} }
} }
}
@@ -2,14 +2,14 @@ package com.rtbishop.look4sat.presentation.sourcesScreen
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData import androidx.lifecycle.liveData
import com.rtbishop.look4sat.domain.SatelliteRepo import com.rtbishop.look4sat.domain.DataRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SourcesViewModel @Inject constructor(satelliteRepo: SatelliteRepo) : ViewModel() { class SourcesViewModel @Inject constructor(dataRepository: DataRepository) : ViewModel() {
val sources = liveData { val sources = liveData {
emit(satelliteRepo.getSavedSources()) emit(dataRepository.getWebSources())
} }
} }
@@ -17,62 +17,49 @@
*/ */
package com.rtbishop.look4sat.data package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.SatelliteRepo import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.domain.model.SatEntry import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.Transmitter
import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.domain.predict.TLE import com.rtbishop.look4sat.domain.predict.TLE
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.InputStream import java.io.InputStream
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
class DataRepository( class DefaultRepository(
private val localSource: LocalDataSource, private val localSource: LocalDataSource,
private val remoteSource: RemoteDataSource, private val remoteSource: RemoteDataSource,
private val repoDispatcher: CoroutineDispatcher private val repoDispatcher: CoroutineDispatcher
) : SatelliteRepo { ) : DataRepository {
override fun getSatItems(): Flow<List<SatItem>> { override val defaultSelection = listOf(43700, 25544, 25338, 28654, 33591, 40069, 27607, 24278)
return localSource.getEntriesWithModes() override val defaultSources = listOf(
}
override fun getTransmitters(catnum: Int): Flow<List<Transmitter>> {
return localSource.getTransmitters(catnum)
}
override fun getDefaultSources(): List<String> {
return listOf(
"https://celestrak.com/NORAD/elements/active.txt", "https://celestrak.com/NORAD/elements/active.txt",
"https://amsat.org/tle/current/nasabare.txt", "https://amsat.org/tle/current/nasabare.txt",
"https://www.prismnet.com/~mmccants/tles/classfd.zip", "https://www.prismnet.com/~mmccants/tles/classfd.zip",
"https://www.prismnet.com/~mmccants/tles/inttles.zip" "https://www.prismnet.com/~mmccants/tles/inttles.zip"
) )
override fun getSatelliteItems() = localSource.getSatelliteItems()
override suspend fun getSelectedSatellites() = localSource.getSelectedSatellites()
override suspend fun getTransmitters(catnum: Int) = localSource.getTransmitters(catnum)
override suspend fun getWebSources() = localSource.getWebSources().also { sources ->
return if (sources.isNotEmpty()) sources
else defaultSources
} }
override suspend fun getSavedSources(): List<String> { override suspend fun updateDataFromFile(stream: InputStream) = withContext(repoDispatcher) {
val savedSources = localSource.getSources() localSource.updateEntries(importSatellites(stream))
return if (savedSources.isEmpty()) {
getDefaultSources()
} else savedSources
} }
override suspend fun getSelectedSatellites(): List<Satellite> { override suspend fun updateDataFromWeb(sources: List<String>) {
return localSource.getSelectedSatellites()
}
override suspend fun updateEntriesFromFile(stream: InputStream) = withContext(repoDispatcher) {
localSource.updateEntries(importSatEntries(stream))
}
override suspend fun updateEntriesFromWeb(sources: List<String>) {
coroutineScope { coroutineScope {
launch(repoDispatcher) { launch(repoDispatcher) {
localSource.updateSources(sources) localSource.updateWebSources(sources)
} }
launch(repoDispatcher) { launch(repoDispatcher) {
val streams = mutableListOf<InputStream>() val streams = mutableListOf<InputStream>()
@@ -87,7 +74,7 @@ class DataRepository(
} }
} }
} }
streams.forEach { stream -> entries.addAll(importSatEntries(stream)) } streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
localSource.updateEntries(entries) localSource.updateEntries(entries)
} }
launch(repoDispatcher) { launch(repoDispatcher) {
@@ -97,11 +84,11 @@ class DataRepository(
} }
} }
override suspend fun updateEntriesSelection(catnums: List<Int>, isSelected: Boolean) { override suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean) {
localSource.updateEntriesSelection(catnums, isSelected) localSource.updateSelection(catnums, isSelected)
} }
private fun importSatEntries(stream: InputStream): List<SatEntry> { private fun importSatellites(stream: InputStream): List<SatEntry> {
return TLE.parseTleStream(stream).map { tle -> SatEntry(tle) } return TLE.parseTleStream(stream).map { tle -> SatEntry(tle) }
} }
} }
@@ -25,22 +25,19 @@ import kotlinx.coroutines.flow.Flow
interface LocalDataSource { interface LocalDataSource {
// Entries fun getSatelliteItems(): Flow<List<SatItem>>
fun getEntriesWithModes(): Flow<List<SatItem>>
suspend fun getSelectedSatellites(): List<Satellite> suspend fun getSelectedSatellites(): List<Satellite>
suspend fun getTransmitters(catnum: Int): List<Transmitter>
suspend fun getWebSources(): List<String>
suspend fun updateEntries(entries: List<SatEntry>) suspend fun updateEntries(entries: List<SatEntry>)
suspend fun updateEntriesSelection(catnums: List<Int>, isSelected: Boolean) suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean)
// Sources
suspend fun getSources(): List<String>
suspend fun updateSources(sources: List<String>)
// Transmitters
fun getTransmitters(catnum: Int): Flow<List<Transmitter>>
suspend fun updateTransmitters(transmitters: List<Transmitter>) suspend fun updateTransmitters(transmitters: List<Transmitter>)
suspend fun updateWebSources(sources: List<String>)
} }
@@ -23,21 +23,22 @@ import com.rtbishop.look4sat.domain.predict.Satellite
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import java.io.InputStream import java.io.InputStream
interface SatelliteRepo { interface DataRepository {
fun getSatItems(): Flow<List<SatItem>> val defaultSelection: List<Int>
val defaultSources: List<String>
fun getTransmitters(catnum: Int): Flow<List<Transmitter>> fun getSatelliteItems(): Flow<List<SatItem>>
fun getDefaultSources(): List<String>
suspend fun getSavedSources(): List<String>
suspend fun getSelectedSatellites(): List<Satellite> suspend fun getSelectedSatellites(): List<Satellite>
suspend fun updateEntriesFromFile(stream: InputStream) suspend fun getTransmitters(catnum: Int): List<Transmitter>
suspend fun updateEntriesFromWeb(sources: List<String>) suspend fun getWebSources(): List<String>
suspend fun updateEntriesSelection(catnums: List<Int>, isSelected: Boolean) suspend fun updateDataFromFile(stream: InputStream)
suspend fun updateDataFromWeb(sources: List<String> = defaultSources)
suspend fun updateSelection(catnums: List<Int> = defaultSelection, isSelected: Boolean = true)
} }
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.rtbishop.look4sat.domain package com.rtbishop.look4sat.domain.model
sealed class DataState<out T> { sealed class DataState<out T> {
data class Success<out T>(val data: T) : DataState<T>() data class Success<out T>(val data: T) : DataState<T>()
-1
View File
@@ -20,4 +20,3 @@ android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
kapt.use.worker.api=false kapt.use.worker.api=false
org.gradle.unsafe.configuration-cache=true