Renamed and moved several classes and interfaces

This commit is contained in:
Arty Bishop committed 2021-10-27 21:47:02 +01:00
1 parent 84240c94c6
commit b6d019ace7
24 files changed
+155 -153

No files matched your search

@@ -24,7 +24,7 @@ import android.hardware.SensorManager
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.round import kotlin.math.round
class OrientationProvider @Inject constructor(private val sensorManager: SensorManager) : class OrientationSource @Inject constructor(private val sensorManager: SensorManager) :
SensorEventListener { SensorEventListener {
interface OrientationListener { interface OrientationListener {
@@ -21,7 +21,7 @@ import android.content.SharedPreferences
import android.hardware.GeomagneticField import android.hardware.GeomagneticField
import android.location.LocationManager import android.location.LocationManager
import androidx.core.content.edit import androidx.core.content.edit
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.domain.GeoPos import com.rtbishop.look4sat.domain.GeoPos
import com.rtbishop.look4sat.domain.QthConverter import com.rtbishop.look4sat.domain.QthConverter
import com.rtbishop.look4sat.utility.round import com.rtbishop.look4sat.utility.round
@@ -29,11 +29,11 @@ import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
import javax.inject.Inject import javax.inject.Inject
class PreferencesProvider @Inject constructor( class PreferencesSource @Inject constructor(
moshi: Moshi, moshi: Moshi,
private val locationManager: LocationManager, private val locationManager: LocationManager,
private val preferences: SharedPreferences private val preferences: SharedPreferences
) : PreferencesSource { ) : Preferences {
private val sourcesType = Types.newParameterizedType(List::class.java, String::class.java) private val sourcesType = Types.newParameterizedType(List::class.java, String::class.java)
private val sourcesAdapter = moshi.adapter<List<String>>(sourcesType) private val sourcesAdapter = moshi.adapter<List<String>>(sourcesType)
@@ -17,18 +17,18 @@
*/ */
package com.rtbishop.look4sat.framework.api package com.rtbishop.look4sat.framework.api
import com.rtbishop.look4sat.data.RemoteDataSource import com.rtbishop.look4sat.data.RemoteSource
import com.rtbishop.look4sat.domain.Transmitter import com.rtbishop.look4sat.domain.Transmitter
import com.rtbishop.look4sat.utility.DataMapper import com.rtbishop.look4sat.utility.DataMapper
import java.io.InputStream import java.io.InputStream
class RemoteSource(private val api: SatelliteService) : RemoteDataSource { class RemoteDataSource(private val satelliteApi: SatelliteApi) : RemoteSource {
override suspend fun fetchDataStream(url: String): InputStream? { override suspend fun fetchFileStream(url: String): InputStream? {
return api.fetchFileByUrl(url).body()?.byteStream() return satelliteApi.fetchFileStream(url).body()?.byteStream()
} }
override suspend fun fetchTransmitters(): List<Transmitter> { override suspend fun fetchTransmitters(): List<Transmitter> {
return DataMapper.satTransListToDomainTransList(api.fetchTransmitters()) return DataMapper.satTransListToDomainTransList(satelliteApi.fetchTransmitters())
} }
} }
@@ -24,11 +24,11 @@ import retrofit2.http.GET
import retrofit2.http.Streaming import retrofit2.http.Streaming
import retrofit2.http.Url import retrofit2.http.Url
interface SatelliteService { interface SatelliteApi {
@Streaming @Streaming
@GET @GET
suspend fun fetchFileByUrl(@Url url: String): Response<ResponseBody> suspend fun fetchFileStream(@Url url: String): Response<ResponseBody>
@GET("transmitters/") @GET("transmitters/")
suspend fun fetchTransmitters(): List<Transmitter> suspend fun fetchTransmitters(): List<Transmitter>
@@ -17,18 +17,18 @@
*/ */
package com.rtbishop.look4sat.framework.db package com.rtbishop.look4sat.framework.db
import com.rtbishop.look4sat.data.LocalDataSource import com.rtbishop.look4sat.data.LocalSource
import com.rtbishop.look4sat.data.SatEntry import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.data.SatItem import com.rtbishop.look4sat.data.SatItem
import com.rtbishop.look4sat.domain.Transmitter
import com.rtbishop.look4sat.domain.Satellite import com.rtbishop.look4sat.domain.Satellite
import com.rtbishop.look4sat.domain.Transmitter
import com.rtbishop.look4sat.utility.DataMapper import com.rtbishop.look4sat.utility.DataMapper
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
class LocalSource(private val satelliteDao: SatelliteDao) : LocalDataSource { class LocalDataSource(private val satelliteDao: SatelliteDao) : LocalSource {
override fun getSatItems(): Flow<List<SatItem>> { override fun getEntriesWithModes(): Flow<List<SatItem>> {
return satelliteDao.getSatItems() return satelliteDao.getSatItems()
.map { satItems -> DataMapper.satItemsToDomainItems(satItems) } .map { satItems -> DataMapper.satItemsToDomainItems(satItems) }
} }
@@ -46,13 +46,13 @@ class LocalSource(private val satelliteDao: SatelliteDao) : LocalDataSource {
satelliteDao.updateEntriesSelection(catNums, isSelected) satelliteDao.updateEntriesSelection(catNums, isSelected)
} }
override fun getSatTransmitters(catNum: Int): Flow<List<Transmitter>> { override fun getTransmitters(catNum: Int): Flow<List<Transmitter>> {
return satelliteDao.getSatTransmitters(catNum) return satelliteDao.getSatTransmitters(catNum)
.map { satTransList -> DataMapper.satTransListToDomainTransList(satTransList) } .map { satTransList -> DataMapper.satTransListToDomainTransList(satTransList) }
} }
override suspend fun updateTransmitters(satelliteTrans: List<Transmitter>) { override suspend fun updateTransmitters(transmitters: List<Transmitter>) {
val satTransList = DataMapper.domainTransListToSatTransList(satelliteTrans) val satTransList = DataMapper.domainTransListToSatTransList(transmitters)
satelliteDao.updateTransmitters(satTransList) satelliteDao.updateTransmitters(satTransList)
} }
} }
@@ -23,7 +23,7 @@ import com.rtbishop.look4sat.domain.TLE
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
object Converters { object RoomConverters {
private lateinit var paramsAdapter: JsonAdapter<TLE> private lateinit var paramsAdapter: JsonAdapter<TLE>
@@ -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(entities = [SatEntry::class, Transmitter::class], version = 2, exportSchema = true) @Database(entities = [SatEntry::class, Transmitter::class], version = 2, exportSchema = true)
@TypeConverters(Converters::class) @TypeConverters(RoomConverters::class)
abstract class SatelliteDb : RoomDatabase() { abstract class SatelliteDb : RoomDatabase() {
abstract fun satelliteDao(): SatelliteDao abstract fun satelliteDao(): SatelliteDao
@@ -31,7 +31,7 @@ import dagger.hilt.components.SingletonComponent
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
object ApplicationModule { object AndroidModule {
@Provides @Provides
fun provideContentResolver(@ApplicationContext context: Context): ContentResolver { fun provideContentResolver(@ApplicationContext context: Context): ContentResolver {
@@ -21,12 +21,15 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.location.LocationManager import android.location.LocationManager
import androidx.room.Room import androidx.room.Room
import com.rtbishop.look4sat.data.* import com.rtbishop.look4sat.data.LocalSource
import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.data.RemoteSource
import com.rtbishop.look4sat.data.SatelliteRepo
import com.rtbishop.look4sat.domain.PassReporter import com.rtbishop.look4sat.domain.PassReporter
import com.rtbishop.look4sat.domain.Predictor import com.rtbishop.look4sat.domain.Predictor
import com.rtbishop.look4sat.framework.PreferencesProvider import com.rtbishop.look4sat.framework.PreferencesSource
import com.rtbishop.look4sat.framework.api.RemoteSource import com.rtbishop.look4sat.framework.api.RemoteDataSource
import com.rtbishop.look4sat.framework.api.SatelliteService import com.rtbishop.look4sat.framework.api.SatelliteApi
import com.rtbishop.look4sat.framework.db.* import com.rtbishop.look4sat.framework.db.*
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import dagger.Module import dagger.Module
@@ -41,7 +44,7 @@ import javax.inject.Singleton
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
object SatelliteDataModule { object DataModule {
@Provides @Provides
@Singleton @Singleton
@@ -55,8 +58,8 @@ object SatelliteDataModule {
moshi: Moshi, moshi: Moshi,
locationManager: LocationManager, locationManager: LocationManager,
preferences: SharedPreferences preferences: SharedPreferences
): PreferencesSource { ): Preferences {
return PreferencesProvider(moshi, locationManager, preferences) return PreferencesSource(moshi, locationManager, preferences)
} }
@Provides @Provides
@@ -68,17 +71,17 @@ object SatelliteDataModule {
@Provides @Provides
@Singleton @Singleton
fun provideSatelliteRepo( fun provideSatelliteRepo(
preferencesSource: PreferencesSource, preferences: Preferences,
localDataSource: LocalDataSource, localSource: LocalSource,
remoteDataSource: RemoteDataSource, remoteSource: RemoteSource,
@IoDispatcher dispatcher: CoroutineDispatcher @IoDispatcher dispatcher: CoroutineDispatcher
): SatelliteRepo { ): SatelliteRepo {
return SatelliteRepo(preferencesSource, localDataSource, remoteDataSource, dispatcher) return SatelliteRepo(preferences, localSource, remoteSource, dispatcher)
} }
@Provides @Provides
fun provideLocalDataSource(satelliteDao: SatelliteDao): LocalDataSource { fun provideLocalDataSource(satelliteDao: SatelliteDao): LocalSource {
return LocalSource(satelliteDao) return LocalDataSource(satelliteDao)
} }
@Provides @Provides
@@ -90,24 +93,24 @@ object SatelliteDataModule {
@Provides @Provides
@Singleton @Singleton
fun provideSatelliteDb(@ApplicationContext context: Context, moshi: Moshi): SatelliteDb { fun provideSatelliteDb(@ApplicationContext context: Context, moshi: Moshi): SatelliteDb {
Converters.initialize(moshi) RoomConverters.initialize(moshi)
return Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb") return Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb")
.addMigrations(MIGRATION_1_2).build() .addMigrations(MIGRATION_1_2).build()
} }
@Provides @Provides
@Singleton @Singleton
fun provideRemoteDataSource(satelliteService: SatelliteService): RemoteDataSource { fun provideRemoteDataSource(satelliteApi: SatelliteApi): RemoteSource {
return RemoteSource(satelliteService) return RemoteDataSource(satelliteApi)
} }
@Provides @Provides
@Singleton @Singleton
fun provideSatelliteService(): SatelliteService { fun provideSatelliteService(): SatelliteApi {
return Retrofit.Builder() return Retrofit.Builder()
.baseUrl("https://db.satnogs.org/api/") .baseUrl("https://db.satnogs.org/api/")
.addConverterFactory(MoshiConverterFactory.create()) .addConverterFactory(MoshiConverterFactory.create())
.build().create(SatelliteService::class.java) .build().create(SatelliteApi::class.java)
} }
@Provides @Provides
@@ -21,7 +21,7 @@ import android.content.ContentResolver
import android.net.Uri import android.net.Uri
import android.widget.SearchView import android.widget.SearchView
import androidx.lifecycle.* import androidx.lifecycle.*
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.data.SatelliteRepo import com.rtbishop.look4sat.data.SatelliteRepo
import com.rtbishop.look4sat.data.SatItem import com.rtbishop.look4sat.data.SatItem
import com.rtbishop.look4sat.framework.model.Result import com.rtbishop.look4sat.framework.model.Result
@@ -33,7 +33,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class EntriesViewModel @Inject constructor( class EntriesViewModel @Inject constructor(
private val preferencesSource: PreferencesSource, private val preferences: Preferences,
private val resolver: ContentResolver, private val resolver: ContentResolver,
private val satelliteRepo: SatelliteRepo, private val satelliteRepo: SatelliteRepo,
) : ViewModel(), EntriesAdapter.EntriesClickListener, SearchView.OnQueryTextListener { ) : ViewModel(), EntriesAdapter.EntriesClickListener, SearchView.OnQueryTextListener {
@@ -67,7 +67,7 @@ class EntriesViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
_satData.value = Result.InProgress _satData.value = Result.InProgress
try { try {
preferencesSource.saveTleSources(sources) preferences.saveTleSources(sources)
satelliteRepo.updateEntriesFromWeb(sources) satelliteRepo.updateEntriesFromWeb(sources)
} catch (exception: Exception) { } catch (exception: Exception) {
_satData.value = Result.Error(exception) _satData.value = Result.Error(exception)
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.presentation.mapScreen
import androidx.lifecycle.* import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.Predictor import com.rtbishop.look4sat.domain.Predictor
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.data.SatelliteRepo import com.rtbishop.look4sat.data.SatelliteRepo
import com.rtbishop.look4sat.domain.* import com.rtbishop.look4sat.domain.*
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@@ -33,7 +33,7 @@ import kotlin.math.min
class MapViewModel @Inject constructor( class MapViewModel @Inject constructor(
private val satelliteRepo: SatelliteRepo, private val satelliteRepo: SatelliteRepo,
private val predictor: Predictor, private val predictor: Predictor,
private val preferences: PreferencesSource, private val preferences: Preferences,
) : ViewModel() { ) : ViewModel() {
private val stationPos = preferences.loadStationPosition() private val stationPos = preferences.loadStationPosition()
@@ -21,7 +21,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.data.SatelliteRepo import com.rtbishop.look4sat.data.SatelliteRepo
import com.rtbishop.look4sat.domain.Predictor import com.rtbishop.look4sat.domain.Predictor
import com.rtbishop.look4sat.domain.SatPass import com.rtbishop.look4sat.domain.SatPass
@@ -36,7 +36,7 @@ import javax.inject.Inject
class PassesViewModel @Inject constructor( class PassesViewModel @Inject constructor(
private val satelliteRepo: SatelliteRepo, private val satelliteRepo: SatelliteRepo,
private val predictor: Predictor, private val predictor: Predictor,
private val preferencesSource: PreferencesSource private val preferences: Preferences
) : ViewModel() { ) : ViewModel() {
private val _passes = MutableLiveData<Result<List<SatPass>>>(Result.InProgress) private val _passes = MutableLiveData<Result<List<SatPass>>>(Result.InProgress)
@@ -46,14 +46,14 @@ class PassesViewModel @Inject constructor(
val isFirstLaunchDone: LiveData<Boolean> = _isFirstLaunchDone val isFirstLaunchDone: LiveData<Boolean> = _isFirstLaunchDone
init { init {
if (preferencesSource.isSetupDone()) { if (preferences.isSetupDone()) {
viewModelScope.launch { viewModelScope.launch {
_passes.postValue(Result.InProgress) _passes.postValue(Result.InProgress)
val dateNow = Date() val dateNow = Date()
val satellites = satelliteRepo.getSelectedSatellites() val satellites = satelliteRepo.getSelectedSatellites()
val stationPos = preferencesSource.loadStationPosition() val stationPos = preferences.loadStationPosition()
val hoursAhead = preferencesSource.getHoursAhead() val hoursAhead = preferences.getHoursAhead()
val minElev = preferencesSource.getMinElevation() val minElev = preferences.getMinElevation()
predictor.triggerCalculation(satellites, stationPos, dateNow, hoursAhead, minElev) predictor.triggerCalculation(satellites, stationPos, dateNow, hoursAhead, minElev)
} }
} else { } else {
@@ -68,19 +68,19 @@ class PassesViewModel @Inject constructor(
} }
fun triggerInitialSetup() { fun triggerInitialSetup() {
preferencesSource.updatePositionFromGPS() preferences.updatePositionFromGPS()
viewModelScope.launch { viewModelScope.launch {
_passes.postValue(Result.InProgress) _passes.postValue(Result.InProgress)
val defaultCatNums = listOf(43700, 25544, 25338, 28654, 33591, 40069, 27607, 24278) val defaultCatNums = listOf(43700, 25544, 25338, 28654, 33591, 40069, 27607, 24278)
val dateNow = Date() val dateNow = Date()
val satellites = satelliteRepo.getSelectedSatellites() val satellites = satelliteRepo.getSelectedSatellites()
val stationPos = preferencesSource.loadStationPosition() val stationPos = preferences.loadStationPosition()
val hoursAhead = preferencesSource.getHoursAhead() val hoursAhead = preferences.getHoursAhead()
val minElev = preferencesSource.getMinElevation() val minElev = preferences.getMinElevation()
satelliteRepo.updateEntriesFromWeb(preferencesSource.loadDefaultSources()) satelliteRepo.updateEntriesFromWeb(preferences.loadDefaultSources())
satelliteRepo.updateEntriesSelection(defaultCatNums, true) satelliteRepo.updateEntriesSelection(defaultCatNums, true)
predictor.forceCalculation(satellites, stationPos, dateNow, hoursAhead, minElev) predictor.forceCalculation(satellites, stationPos, dateNow, hoursAhead, minElev)
preferencesSource.setSetupDone() preferences.setSetupDone()
_isFirstLaunchDone.value = true _isFirstLaunchDone.value = true
} }
} }
@@ -91,15 +91,15 @@ class PassesViewModel @Inject constructor(
passesProcessing?.cancelAndJoin() passesProcessing?.cancelAndJoin()
val dateNow = Date() val dateNow = Date()
val satellites = satelliteRepo.getSelectedSatellites() val satellites = satelliteRepo.getSelectedSatellites()
val stationPos = preferencesSource.loadStationPosition() val stationPos = preferences.loadStationPosition()
val hoursAhead = preferencesSource.getHoursAhead() val hoursAhead = preferences.getHoursAhead()
val minElev = preferencesSource.getMinElevation() val minElev = preferences.getMinElevation()
predictor.forceCalculation(satellites, stationPos, dateNow, hoursAhead, minElev) predictor.forceCalculation(satellites, stationPos, dateNow, hoursAhead, minElev)
} }
} }
fun shouldUseUTC(): Boolean { fun shouldUseUTC(): Boolean {
return preferencesSource.shouldUseUTC() return preferences.shouldUseUTC()
} }
private suspend fun tickPasses(passes: List<SatPass>) = withContext(Dispatchers.Default) { private suspend fun tickPasses(passes: List<SatPass>) = withContext(Dispatchers.Default) {
@@ -25,7 +25,7 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.databinding.FragmentRadarBinding import com.rtbishop.look4sat.databinding.FragmentRadarBinding
import com.rtbishop.look4sat.domain.SatPass import com.rtbishop.look4sat.domain.SatPass
import com.rtbishop.look4sat.domain.SatPos import com.rtbishop.look4sat.domain.SatPos
@@ -39,7 +39,7 @@ import javax.inject.Inject
class RadarFragment : Fragment(R.layout.fragment_radar) { class RadarFragment : Fragment(R.layout.fragment_radar) {
@Inject @Inject
lateinit var preferences: PreferencesSource lateinit var preferences: Preferences
private val viewModel: RadarViewModel by viewModels() private val viewModel: RadarViewModel by viewModels()
private var radarView: RadarView? = null private var radarView: RadarView? = null
@@ -19,13 +19,13 @@ package com.rtbishop.look4sat.presentation.radarScreen
import androidx.lifecycle.* import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.PassReporter import com.rtbishop.look4sat.domain.PassReporter
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.data.SatelliteRepo import com.rtbishop.look4sat.data.SatelliteRepo
import com.rtbishop.look4sat.domain.Predictor import com.rtbishop.look4sat.domain.Predictor
import com.rtbishop.look4sat.domain.SatPass import com.rtbishop.look4sat.domain.SatPass
import com.rtbishop.look4sat.domain.SatPos import com.rtbishop.look4sat.domain.SatPos
import com.rtbishop.look4sat.domain.Transmitter import com.rtbishop.look4sat.domain.Transmitter
import com.rtbishop.look4sat.framework.OrientationProvider import com.rtbishop.look4sat.framework.OrientationSource
import com.rtbishop.look4sat.utility.round import com.rtbishop.look4sat.utility.round
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -37,12 +37,12 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class RadarViewModel @Inject constructor( class RadarViewModel @Inject constructor(
private val orientationProvider: OrientationProvider, private val orientationSource: OrientationSource,
private val preferences: PreferencesSource, private val preferences: Preferences,
private val predictor: Predictor, private val predictor: Predictor,
private val satelliteRepo: SatelliteRepo, private val satelliteRepo: SatelliteRepo,
private val passReporter: PassReporter private val passReporter: PassReporter
) : ViewModel(), OrientationProvider.OrientationListener { ) : ViewModel(), OrientationSource.OrientationListener {
private val stationPos = preferences.loadStationPosition() private val stationPos = preferences.loadStationPosition()
private val _passData = MutableLiveData<RadarData>() private val _passData = MutableLiveData<RadarData>()
@@ -64,11 +64,11 @@ class RadarViewModel @Inject constructor(
} }
fun enableSensor() { fun enableSensor() {
if (preferences.shouldUseCompass()) orientationProvider.startListening(this) if (preferences.shouldUseCompass()) orientationSource.startListening(this)
} }
fun disableSensor() { fun disableSensor() {
if (preferences.shouldUseCompass()) orientationProvider.stopListening() if (preferences.shouldUseCompass()) orientationSource.stopListening()
} }
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) { override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
@@ -29,8 +29,8 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.framework.PreferencesProvider import com.rtbishop.look4sat.framework.PreferencesSource
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
@@ -38,7 +38,7 @@ import javax.inject.Inject
class SettingsFragment : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragmentCompat() {
@Inject @Inject
lateinit var preferencesSource: PreferencesSource lateinit var preferences: Preferences
private val requestPermissionLauncher = private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
@@ -56,20 +56,20 @@ class SettingsFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
findPreference<Preference>(PreferencesProvider.keyPositionGPS)?.apply { findPreference<Preference>(PreferencesSource.keyPositionGPS)?.apply {
setOnPreferenceClickListener { setOnPreferenceClickListener {
updatePositionFromGPS() updatePositionFromGPS()
return@setOnPreferenceClickListener true return@setOnPreferenceClickListener true
} }
} }
findPreference<Preference>(PreferencesProvider.keyPositionQTH)?.apply { findPreference<Preference>(PreferencesSource.keyPositionQTH)?.apply {
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
updatePositionFromQth(newValue.toString()) updatePositionFromQth(newValue.toString())
} }
} }
findPreference<EditTextPreference>(PreferencesProvider.keyRotatorAddress)?.apply { findPreference<EditTextPreference>(PreferencesSource.keyRotatorAddress)?.apply {
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
val ip4 = "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!\$)|\$)){4}\$" val ip4 = "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!\$)|\$)){4}\$"
if (newValue.toString().matches(ip4.toRegex())) { if (newValue.toString().matches(ip4.toRegex())) {
@@ -81,7 +81,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
} }
} }
findPreference<EditTextPreference>(PreferencesProvider.keyRotatorPort)?.apply { findPreference<EditTextPreference>(PreferencesSource.keyRotatorPort)?.apply {
setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_NUMBER } setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_NUMBER }
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
val portValue = newValue.toString() val portValue = newValue.toString()
@@ -96,7 +96,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
} }
private fun updatePositionFromQth(qthString: String): Boolean { private fun updatePositionFromQth(qthString: String): Boolean {
return if (preferencesSource.updatePositionFromQTH(qthString)) { return if (preferences.updatePositionFromQTH(qthString)) {
showSnack(getString(R.string.pref_pos_success)) showSnack(getString(R.string.pref_pos_success))
true true
} else { } else {
@@ -109,7 +109,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
val locPermString = Manifest.permission.ACCESS_FINE_LOCATION val locPermString = Manifest.permission.ACCESS_FINE_LOCATION
val locPermResult = ContextCompat.checkSelfPermission(requireContext(), locPermString) val locPermResult = ContextCompat.checkSelfPermission(requireContext(), locPermString)
if (locPermResult == PackageManager.PERMISSION_GRANTED) { if (locPermResult == PackageManager.PERMISSION_GRANTED) {
if (preferencesSource.updatePositionFromGPS()) { if (preferences.updatePositionFromGPS()) {
showSnack(getString(R.string.pref_pos_success)) showSnack(getString(R.string.pref_pos_success))
} else { } else {
showSnack(getString(R.string.pref_pos_gps_null)) showSnack(getString(R.string.pref_pos_gps_null))
@@ -25,7 +25,7 @@ import android.view.WindowManager
import androidx.appcompat.app.AppCompatDialogFragment import androidx.appcompat.app.AppCompatDialogFragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.PreferencesSource import com.rtbishop.look4sat.data.Preferences
import com.rtbishop.look4sat.databinding.DialogSourcesBinding import com.rtbishop.look4sat.databinding.DialogSourcesBinding
import com.rtbishop.look4sat.utility.setNavResult import com.rtbishop.look4sat.utility.setNavResult
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -35,7 +35,7 @@ import javax.inject.Inject
class SourcesDialog : AppCompatDialogFragment() { class SourcesDialog : AppCompatDialogFragment() {
@Inject @Inject
lateinit var prefsManager: PreferencesSource lateinit var prefsManager: Preferences
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
return inflater.inflate(R.layout.dialog_sources, group, false) return inflater.inflate(R.layout.dialog_sources, group, false)
@@ -21,11 +21,11 @@ import com.rtbishop.look4sat.domain.Transmitter
import com.rtbishop.look4sat.domain.Satellite import com.rtbishop.look4sat.domain.Satellite
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface LocalDataSource { interface LocalSource {
fun getSatItems(): Flow<List<SatItem>> fun getEntriesWithModes(): Flow<List<SatItem>>
fun getSatTransmitters(catNum: Int): Flow<List<Transmitter>> fun getTransmitters(catNum: Int): Flow<List<Transmitter>>
suspend fun getSelectedSatellites(): List<Satellite> suspend fun getSelectedSatellites(): List<Satellite>
@@ -33,5 +33,5 @@ interface LocalDataSource {
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean)
suspend fun updateTransmitters(satelliteTrans: List<Transmitter>) suspend fun updateTransmitters(transmitters: List<Transmitter>)
} }
@@ -19,7 +19,7 @@ package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.GeoPos import com.rtbishop.look4sat.domain.GeoPos
interface PreferencesSource { interface Preferences {
fun positionToQTH(lat: Double, lon: Double): String? fun positionToQTH(lat: Double, lon: Double): String?
@@ -20,9 +20,9 @@ package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.Transmitter import com.rtbishop.look4sat.domain.Transmitter
import java.io.InputStream import java.io.InputStream
interface RemoteDataSource { interface RemoteSource {
suspend fun fetchDataStream(url: String): InputStream? suspend fun fetchFileStream(url: String): InputStream?
suspend fun fetchTransmitters(): List<Transmitter> suspend fun fetchTransmitters(): List<Transmitter>
} }
@@ -29,43 +29,43 @@ import java.io.InputStream
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
class SatelliteRepo( class SatelliteRepo(
private val preferencesSource: PreferencesSource, private val preferences: Preferences,
private val localDataSource: LocalDataSource, private val localSource: LocalSource,
private val remoteDataSource: RemoteDataSource, private val remoteSource: RemoteSource,
private val ioDispatcher: CoroutineDispatcher private val repoDispatcher: CoroutineDispatcher
) { ) {
fun saveSelectedModes(modes: List<String>) { fun saveSelectedModes(modes: List<String>) {
preferencesSource.saveModesSelection(modes) preferences.saveModesSelection(modes)
} }
fun loadSelectedModes(): List<String> { fun loadSelectedModes(): List<String> {
return preferencesSource.loadModesSelection() return preferences.loadModesSelection()
} }
fun getSatItems(): Flow<List<SatItem>> { fun getSatItems(): Flow<List<SatItem>> {
return localDataSource.getSatItems() return localSource.getEntriesWithModes()
} }
fun getSatTransmitters(catNum: Int): Flow<List<Transmitter>> { fun getSatTransmitters(catNum: Int): Flow<List<Transmitter>> {
return localDataSource.getSatTransmitters(catNum) return localSource.getTransmitters(catNum)
} }
suspend fun getSelectedSatellites(): List<Satellite> { suspend fun getSelectedSatellites(): List<Satellite> {
return localDataSource.getSelectedSatellites() return localSource.getSelectedSatellites()
} }
suspend fun updateEntriesFromFile(stream: InputStream) = withContext(ioDispatcher) { suspend fun updateEntriesFromFile(stream: InputStream) = withContext(repoDispatcher) {
localDataSource.updateEntries(importSatEntries(stream)) localSource.updateEntries(importSatEntries(stream))
} }
suspend fun updateEntriesFromWeb(sources: List<String>) { suspend fun updateEntriesFromWeb(sources: List<String>) {
coroutineScope { coroutineScope {
launch(ioDispatcher) { launch(repoDispatcher) {
val streams = mutableListOf<InputStream>() val streams = mutableListOf<InputStream>()
val entries = mutableListOf<SatEntry>() val entries = mutableListOf<SatEntry>()
sources.forEach { source -> sources.forEach { source ->
remoteDataSource.fetchDataStream(source)?.let { stream -> remoteSource.fetchFileStream(source)?.let { stream ->
if (source.contains(".zip", true)) { if (source.contains(".zip", true)) {
val zipStream = ZipInputStream(stream).apply { nextEntry } val zipStream = ZipInputStream(stream).apply { nextEntry }
streams.add(zipStream) streams.add(zipStream)
@@ -75,20 +75,20 @@ class SatelliteRepo(
} }
} }
streams.forEach { stream -> entries.addAll(importSatEntries(stream)) } streams.forEach { stream -> entries.addAll(importSatEntries(stream)) }
localDataSource.updateEntries(entries) localSource.updateEntries(entries)
} }
launch(ioDispatcher) { launch(repoDispatcher) {
val transmitters = remoteDataSource.fetchTransmitters().filter { it.isAlive } val transmitters = remoteSource.fetchTransmitters().filter { it.isAlive }
localDataSource.updateTransmitters(transmitters) localSource.updateTransmitters(transmitters)
} }
} }
} }
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) { suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
localDataSource.updateEntriesSelection(catNums, isSelected) localSource.updateEntriesSelection(catNums, isSelected)
} }
private fun importSatEntries(stream: InputStream): List<SatEntry> { private fun importSatEntries(stream: InputStream): List<SatEntry> {
return TLE.parseStream(stream).map { tle -> SatEntry(tle) } return TLE.parseTleStream(stream).map { tle -> SatEntry(tle) }
} }
} }
@@ -50,35 +50,32 @@ data class SatPos(
fun getRangeCircle(): List<GeoPos> { fun getRangeCircle(): List<GeoPos> {
val positions = mutableListOf<GeoPos>() val positions = mutableListOf<GeoPos>()
val lat = this.latitude
val lon = this.longitude
// rangeCircleRadiusKm
// earthRadiusKm * acos(earthRadiusKm / (earthRadiusKm + satPos.altitude))
val beta = acos(earthRadiusKm / (earthRadiusKm + this.altitude)) val beta = acos(earthRadiusKm / (earthRadiusKm + this.altitude))
// val radiusKm = earthRadiusKm * acos(earthRadiusKm / (earthRadiusKm + altitude))
var tempAzimuth = 0 var tempAzimuth = 0
while (tempAzimuth < 360) { while (tempAzimuth < 360) {
val azimuth = tempAzimuth / 360.0 * 2.0 * Math.PI val azimuth = tempAzimuth / 360.0 * 2.0 * Math.PI
var rangelat = asin(sin(lat) * cos(beta) + cos(azimuth) * sin(beta) * cos(lat)) var lat = asin(sin(latitude) * cos(beta) + cos(azimuth) * sin(beta) * cos(latitude))
val num = (cos(beta) - (sin(lat) * sin(rangelat))) val num = (cos(beta) - (sin(latitude) * sin(lat)))
val den = cos(lat) * cos(rangelat) val den = cos(latitude) * cos(lat)
var rangelon = if (tempAzimuth == 0 && (beta > ((Math.PI / 2.0) - lat))) { var lon = if (tempAzimuth == 0 && (beta > ((Math.PI / 2.0) - latitude))) {
lon + Math.PI longitude + Math.PI
} else if (tempAzimuth == 180 && (beta > ((Math.PI / 2.0) - lat))) { } else if (tempAzimuth == 180 && (beta > ((Math.PI / 2.0) - latitude))) {
lon + Math.PI longitude + Math.PI
} else if (abs(num / den) > 1.0) { } else if (abs(num / den) > 1.0) {
lon longitude
} else { } else {
if ((180 - tempAzimuth) >= 0) { if ((180 - tempAzimuth) >= 0) {
lon - acos(num / den) longitude - acos(num / den)
} else { } else {
lon + acos(num / den) longitude + acos(num / den)
} }
} }
while (rangelon < 0.0) rangelon += Math.PI * 2.0 while (lon < 0.0) lon += Math.PI * 2.0
while (rangelon > Math.PI * 2.0) rangelon -= Math.PI * 2.0 while (lon > Math.PI * 2.0) lon -= Math.PI * 2.0
rangelat = Math.toDegrees(rangelat) lat = Math.toDegrees(lat)
rangelon = Math.toDegrees(rangelon) lon = Math.toDegrees(lon)
positions.add(GeoPos(rangelat, rangelon)) positions.add(GeoPos(lat, lon))
tempAzimuth += 1 tempAzimuth += 1
} }
return positions return positions
@@ -48,39 +48,41 @@ data class TLE(
companion object { companion object {
fun parseStream(stream: InputStream): List<TLE> { fun parseTleStream(stream: InputStream): List<TLE> {
val itemParams = mutableListOf(String(), String(), String()) val tleStrings = mutableListOf(String(), String(), String())
val importedItems = mutableListOf<TLE>() val parsedItems = mutableListOf<TLE>()
var lineIndex = 0 var lineIndex = 0
stream.bufferedReader().forEachLine { line -> stream.bufferedReader().forEachLine { line ->
itemParams[lineIndex] = line tleStrings[lineIndex] = line
if (lineIndex < 2) { if (lineIndex < 2) {
lineIndex++ lineIndex++
} else { } else {
val isLineOneValid = itemParams[1].substring(0, 1) == "1" val isLineOneValid = tleStrings[1].substring(0, 1) == "1"
val isLineTwoValid = itemParams[2].substring(0, 1) == "2" val isLineTwoValid = tleStrings[2].substring(0, 1) == "2"
if (!isLineOneValid && !isLineTwoValid) return@forEachLine if (!isLineOneValid && !isLineTwoValid) return@forEachLine
parseParams(itemParams)?.let { tle -> importedItems.add(tle) } parseTleStrings(tleStrings)?.let { tle -> parsedItems.add(tle) }
lineIndex = 0 lineIndex = 0
} }
} }
return importedItems return parsedItems
} }
private fun parseParams(params: List<String>): TLE? { private fun parseTleStrings(tleStrings: List<String>): TLE? {
if (params[1].substring(0, 1) != "1" && params[2].substring(0, 1) != "2") return null if (tleStrings[1].substring(0, 1) != "1" && tleStrings[2].substring(0, 1) != "2") {
return null
}
try { try {
val name: String = params[0].trim() val name: String = tleStrings[0].trim()
val epoch: Double = params[1].substring(18, 32).toDouble() val epoch: Double = tleStrings[1].substring(18, 32).toDouble()
val meanmo: Double = params[2].substring(52, 63).toDouble() val meanmo: Double = tleStrings[2].substring(52, 63).toDouble()
val eccn: Double = 1.0e-07 * params[2].substring(26, 33).toDouble() val eccn: Double = 1.0e-07 * tleStrings[2].substring(26, 33).toDouble()
val incl: Double = params[2].substring(8, 16).toDouble() val incl: Double = tleStrings[2].substring(8, 16).toDouble()
val raan: Double = params[2].substring(17, 25).toDouble() val raan: Double = tleStrings[2].substring(17, 25).toDouble()
val argper: Double = params[2].substring(34, 42).toDouble() val argper: Double = tleStrings[2].substring(34, 42).toDouble()
val meanan: Double = params[2].substring(43, 51).toDouble() val meanan: Double = tleStrings[2].substring(43, 51).toDouble()
val catnum: Int = params[1].substring(2, 7).trim().toInt() val catnum: Int = tleStrings[1].substring(2, 7).trim().toInt()
val bstar: Double = 1.0e-5 * params[1].substring(53, 59).toDouble() / val bstar: Double = 1.0e-5 * tleStrings[1].substring(53, 59).toDouble() /
10.0.pow(params[1].substring(60, 61).toDouble()) 10.0.pow(tleStrings[1].substring(60, 61).toDouble())
return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar) return TLE(name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar)
} catch (exception: Exception) { } catch (exception: Exception) {
return null return null
@@ -3,9 +3,9 @@ package com.rtbishop.look4sat
import com.rtbishop.look4sat.domain.TLE import com.rtbishop.look4sat.domain.TLE
import org.junit.Test import org.junit.Test
class Look4SatTest { class TleParserTest {
private val validParamsStream = """ private val validTleStream = """
ISS (ZARYA) ISS (ZARYA)
1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998 1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998
2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085 2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085
@@ -16,7 +16,7 @@ class Look4SatTest {
1 48869U 21057A 21254.57379002 .00002620 00000-0 56399-4 0 9993 1 48869U 21057A 21254.57379002 .00002620 00000-0 56399-4 0 9993
2 48869 51.6436 275.5624 0003404 23.4371 118.9508 15.48629868301984 2 48869 51.6436 275.5624 0003404 23.4371 118.9508 15.48629868301984
""".trimIndent().byteInputStream() """.trimIndent().byteInputStream()
private val invalidParamsStream = """ private val invalidTleStream = """
1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998 1 25544U 98067A 21255.21005818 -.00120443 00000-0 -22592-2 0 9998
2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085 2 25544 51.6451 272.4173 0002526 27.1693 64.4213 15.48396490302085
1 48852U 21053A 21254.65783594 .00009779 00000-0 11349-3 0 9992 1 48852U 21053A 21254.65783594 .00009779 00000-0 11349-3 0 9992
@@ -26,14 +26,14 @@ class Look4SatTest {
""".trimIndent().byteInputStream() """.trimIndent().byteInputStream()
@Test @Test
fun `Given valid params stream returns valid data`() { fun `Given valid TLE stream returns valid data`() {
val parsedList = TLE.parseStream(validParamsStream) val parsedList = TLE.parseTleStream(validTleStream)
assert(parsedList[2].catnum == 48869) assert(parsedList[2].catnum == 48869)
} }
@Test @Test
fun `Given invalid params stream returns empty list`() { fun `Given invalid TLE stream returns empty list`() {
val parsedList = TLE.parseStream(invalidParamsStream) val parsedList = TLE.parseTleStream(invalidTleStream)
assert(parsedList.isEmpty()) assert(parsedList.isEmpty())
} }
} }