Moved DataSources storage to SharedPreferences

This commit is contained in:
Arty Bishop committed 2022-01-09 18:00:25 +00:00
1 parent 0f4a70b204
commit 9c137ada9f
16 files changed
+92 -640

No files matched your search

-7
View File
@@ -26,15 +26,8 @@ android {
} }
} }
// def props = new Properties()
// file("../securityKeys.properties").withInputStream { props.load(it) }
buildTypes { buildTypes {
release { release {
// buildConfigField "String", "ApiKey", props.getProperty("apiKey")
// minifyEnabled true
// shrinkResources true
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
postprocessing { postprocessing {
removeUnusedCode true removeUnusedCode true
removeUnusedResources true removeUnusedResources true
-21
View File
@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -1,134 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "555494c464b5a29285eb5493cc8aa5f6",
"entities": [
{
"tableName": "entries",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tle` TEXT NOT NULL, `catNum` INTEGER NOT NULL, `name` TEXT NOT NULL, `isSelected` INTEGER NOT NULL, PRIMARY KEY(`catNum`))",
"fields": [
{
"fieldPath": "tle",
"columnName": "tle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "catNum",
"columnName": "catNum",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isSelected",
"columnName": "isSelected",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"catNum"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "transmitters",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catNum` INTEGER, PRIMARY KEY(`uuid`))",
"fields": [
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "info",
"columnName": "info",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isAlive",
"columnName": "isAlive",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "downlink",
"columnName": "downlink",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "uplink",
"columnName": "uplink",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "mode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isInverted",
"columnName": "isInverted",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "catNum",
"columnName": "catNum",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"uuid"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "sources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, PRIMARY KEY(`sourceUrl`))",
"fields": [
{
"fieldPath": "sourceUrl",
"columnName": "sourceUrl",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"sourceUrl"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '555494c464b5a29285eb5493cc8aa5f6')"
]
}
}
@@ -1,134 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "555494c464b5a29285eb5493cc8aa5f6",
"entities": [
{
"tableName": "entries",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tle` TEXT NOT NULL, `catNum` INTEGER NOT NULL, `name` TEXT NOT NULL, `isSelected` INTEGER NOT NULL, PRIMARY KEY(`catNum`))",
"fields": [
{
"fieldPath": "tle",
"columnName": "tle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "catNum",
"columnName": "catNum",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isSelected",
"columnName": "isSelected",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"catNum"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "transmitters",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catNum` INTEGER, PRIMARY KEY(`uuid`))",
"fields": [
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "info",
"columnName": "info",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isAlive",
"columnName": "isAlive",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "downlink",
"columnName": "downlink",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "uplink",
"columnName": "uplink",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "mode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isInverted",
"columnName": "isInverted",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "catNum",
"columnName": "catNum",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"uuid"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "sources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, PRIMARY KEY(`sourceUrl`))",
"fields": [
{
"fieldPath": "sourceUrl",
"columnName": "sourceUrl",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"sourceUrl"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '555494c464b5a29285eb5493cc8aa5f6')"
]
}
}
@@ -1,212 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "0f08defa24a78647801005e9277faac7",
"entities": [
{
"tableName": "entries",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isSelected` INTEGER NOT NULL, `name` TEXT NOT NULL, `epoch` REAL NOT NULL, `meanmo` REAL NOT NULL, `eccn` REAL NOT NULL, `incl` REAL NOT NULL, `raan` REAL NOT NULL, `argper` REAL NOT NULL, `meanan` REAL NOT NULL, `catnum` INTEGER NOT NULL, `bstar` REAL NOT NULL, `xincl` REAL NOT NULL, `xnodeo` REAL NOT NULL, `omegao` REAL NOT NULL, `xmo` REAL NOT NULL, `xno` REAL NOT NULL, `isDeepspace` INTEGER NOT NULL, PRIMARY KEY(`catnum`))",
"fields": [
{
"fieldPath": "isSelected",
"columnName": "isSelected",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tle.name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "tle.epoch",
"columnName": "epoch",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.meanmo",
"columnName": "meanmo",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.eccn",
"columnName": "eccn",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.incl",
"columnName": "incl",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.raan",
"columnName": "raan",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.argper",
"columnName": "argper",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.meanan",
"columnName": "meanan",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.catnum",
"columnName": "catnum",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tle.bstar",
"columnName": "bstar",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.xincl",
"columnName": "xincl",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.xnodeo",
"columnName": "xnodeo",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.omegao",
"columnName": "omegao",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.xmo",
"columnName": "xmo",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.xno",
"columnName": "xno",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "tle.isDeepspace",
"columnName": "isDeepspace",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"catnum"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "transmitters",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `info` TEXT NOT NULL, `isAlive` INTEGER NOT NULL, `downlink` INTEGER, `uplink` INTEGER, `mode` TEXT, `isInverted` INTEGER NOT NULL, `catnum` INTEGER, PRIMARY KEY(`uuid`))",
"fields": [
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "info",
"columnName": "info",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isAlive",
"columnName": "isAlive",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "downlink",
"columnName": "downlink",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "uplink",
"columnName": "uplink",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "mode",
"columnName": "mode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isInverted",
"columnName": "isInverted",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "catnum",
"columnName": "catnum",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"uuid"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "sources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, PRIMARY KEY(`sourceUrl`))",
"fields": [
{
"fieldPath": "sourceUrl",
"columnName": "sourceUrl",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"sourceUrl"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0f08defa24a78647801005e9277faac7')"
]
}
}
@@ -22,6 +22,7 @@ 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.BuildConfig import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.data.PreferencesHandler
import com.rtbishop.look4sat.domain.QthConverter import com.rtbishop.look4sat.domain.QthConverter
import com.rtbishop.look4sat.domain.predict.GeoPos import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.presentation.round import com.rtbishop.look4sat.presentation.round
@@ -32,11 +33,12 @@ import javax.inject.Singleton
class PreferencesSource @Inject constructor( class PreferencesSource @Inject constructor(
private val locationManager: LocationManager, private val locationManager: LocationManager,
private val preferences: SharedPreferences private val preferences: SharedPreferences
) { ) : PreferencesHandler {
private val keyInitialSetup = "${BuildConfig.VERSION_NAME}update" private val keyInitialSetup = "${BuildConfig.VERSION_NAME}update"
companion object { companion object {
const val keyDataSources = "dataSources"
const val keyModes = "satModes" const val keyModes = "satModes"
const val keyCompass = "compass" const val keyCompass = "compass"
const val keyRadarSweep = "radarSweep" const val keyRadarSweep = "radarSweep"
@@ -184,4 +186,13 @@ class PreferencesSource @Inject constructor(
fun setRotatorPort(value: String) { fun setRotatorPort(value: String) {
preferences.edit { putString(keyRotatorPort, value) } preferences.edit { putString(keyRotatorPort, value) }
} }
override fun loadDataSources(): List<String> {
val sourcesList = preferences.getStringSet(keyDataSources, null)?.toList()
return if (sourcesList.isNullOrEmpty()) defaultSources else sourcesList
}
override fun saveDataSources(sources: List<String>) {
if (sources.isNotEmpty()) preferences.edit { putStringSet(keyDataSources, sources.toSet()) }
}
} }
@@ -22,14 +22,15 @@ import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.domain.model.SatItem import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.Transmitter import com.rtbishop.look4sat.domain.model.Transmitter
import com.rtbishop.look4sat.domain.predict.Satellite import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.framework.* import com.rtbishop.look4sat.framework.toDomain
import com.rtbishop.look4sat.framework.model.Source import com.rtbishop.look4sat.framework.toDomainItems
import com.rtbishop.look4sat.framework.toFramework
import com.rtbishop.look4sat.framework.toFrameworkEntries
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
class LocalSource( class LocalSource(
private val entriesDao: EntriesDao, private val entriesDao: EntriesDao,
private val sourcesDao: SourcesDao,
private val transmittersDao: TransmittersDao private val transmittersDao: TransmittersDao
) : LocalDataSource { ) : LocalDataSource {
@@ -41,8 +42,6 @@ class LocalSource(
return entriesDao.getSelectedSatellites().map { entry -> entry.tle.createSat() } return entriesDao.getSelectedSatellites().map { entry -> entry.tle.createSat() }
} }
override suspend fun getSources() = sourcesDao.getSources()
override suspend fun getTransmitters(catnum: Int): List<Transmitter> { override suspend fun getTransmitters(catnum: Int): List<Transmitter> {
return transmittersDao.getTransmitters(catnum).toDomain() return transmittersDao.getTransmitters(catnum).toDomain()
} }
@@ -55,10 +54,6 @@ class LocalSource(
entriesDao.updateEntriesSelection(catnums, isSelected) entriesDao.updateEntriesSelection(catnums, isSelected)
} }
override suspend fun updateSources(sources: List<String>) {
sourcesDao.updateSources(sources.map { sourceUrl -> Source(sourceUrl) })
}
override suspend fun updateTransmitters(transmitters: List<Transmitter>) { override suspend fun updateTransmitters(transmitters: List<Transmitter>) {
transmittersDao.updateTransmitters(transmitters.toFramework()) transmittersDao.updateTransmitters(transmitters.toFramework())
} }
@@ -19,50 +19,13 @@ package com.rtbishop.look4sat.framework.local
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.rtbishop.look4sat.framework.model.Source
import com.rtbishop.look4sat.framework.model.SatEntry 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], version = 1)
entities = [SatEntry::class, Source::class, Transmitter::class],
version = 4,
exportSchema = true
)
abstract class SatelliteDb : RoomDatabase() { abstract class SatelliteDb : RoomDatabase() {
abstract fun entriesDao(): EntriesDao abstract fun entriesDao(): EntriesDao
abstract fun sourcesDao(): SourcesDao
abstract fun transmittersDao(): TransmittersDao abstract fun transmittersDao(): TransmittersDao
} }
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE trans_backup (uuid TEXT NOT NULL, info TEXT NOT NULL, isAlive INTEGER NOT NULL, downlink INTEGER, uplink INTEGER, mode TEXT, isInverted INTEGER NOT NULL, catNum INTEGER, PRIMARY KEY(uuid))")
database.execSQL("INSERT INTO trans_backup (uuid, info, isAlive, downlink, uplink, mode, isInverted, catNum) SELECT uuid, info, isAlive, downlink, uplink, mode, isInverted, catNum FROM transmitters")
database.execSQL("DROP TABLE transmitters")
database.execSQL("ALTER TABLE trans_backup RENAME TO transmitters")
}
}
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE sources (sourceUrl TEXT NOT NULL, PRIMARY KEY(sourceUrl))")
}
}
val MIGRATION_3_4 = object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE entries_backup (isSelected INTEGER NOT NULL, name TEXT NOT NULL, epoch REAL NOT NULL, meanmo REAL NOT NULL, eccn REAL NOT NULL, incl REAL NOT NULL, raan REAL NOT NULL, argper REAL NOT NULL, meanan REAL NOT NULL, catnum INTEGER NOT NULL, bstar REAL NOT NULL, xincl REAL NOT NULL, xnodeo REAL NOT NULL, omegao REAL NOT NULL, xmo REAL NOT NULL, xno REAL NOT NULL, isDeepspace INTEGER NOT NULL, PRIMARY KEY(catnum))")
database.execSQL("INSERT INTO entries_backup (isSelected, name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar, xincl, xnodeo, omegao, xmo, xno, isDeepspace) SELECT isSelected, 'name', 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, catNum, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0 FROM entries")
database.execSQL("DROP TABLE entries")
database.execSQL("ALTER TABLE entries_backup RENAME TO entries")
database.execSQL("CREATE TABLE trans_backup (uuid TEXT NOT NULL, info TEXT NOT NULL, isAlive INTEGER NOT NULL, downlink INTEGER, uplink INTEGER, mode TEXT, isInverted INTEGER NOT NULL, catnum INTEGER, PRIMARY KEY(uuid))")
database.execSQL("INSERT INTO trans_backup (uuid, info, isAlive, downlink, uplink, mode, isInverted, catnum) SELECT uuid, info, isAlive, downlink, uplink, mode, isInverted, catNum FROM transmitters")
database.execSQL("DROP TABLE transmitters")
database.execSQL("ALTER TABLE trans_backup RENAME TO transmitters")
}
}
@@ -18,13 +18,18 @@
package com.rtbishop.look4sat.injection package com.rtbishop.look4sat.injection
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.location.LocationManager
import androidx.room.Room import androidx.room.Room
import com.rtbishop.look4sat.data.DefaultRepository import com.rtbishop.look4sat.data.DefaultRepository
import com.rtbishop.look4sat.data.PreferencesHandler
import com.rtbishop.look4sat.domain.DataParser import com.rtbishop.look4sat.domain.DataParser
import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.domain.DataReporter import com.rtbishop.look4sat.domain.DataReporter
import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.domain.predict.Predictor import com.rtbishop.look4sat.domain.predict.Predictor
import com.rtbishop.look4sat.framework.local.* import com.rtbishop.look4sat.framework.PreferencesSource
import com.rtbishop.look4sat.framework.local.LocalSource
import com.rtbishop.look4sat.framework.local.SatelliteDb
import com.rtbishop.look4sat.framework.remote.RemoteSource import com.rtbishop.look4sat.framework.remote.RemoteSource
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@@ -41,16 +46,26 @@ object CoreModule {
@Provides @Provides
@Singleton @Singleton
fun provideSatelliteRepo( fun provideSatelliteRepo(
preferencesHandler: PreferencesHandler,
@ApplicationContext context: Context, @ApplicationContext context: Context,
@IoDispatcher ioDispatcher: CoroutineDispatcher, @IoDispatcher ioDispatcher: CoroutineDispatcher,
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher @DefaultDispatcher defaultDispatcher: CoroutineDispatcher
): DataRepository { ): DataRepository {
val dataParser = DataParser(defaultDispatcher) val dataParser = DataParser(defaultDispatcher)
val db = Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb") val db = Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4).build() .fallbackToDestructiveMigration().build()
val localSource = LocalSource(db.entriesDao(), db.sourcesDao(), db.transmittersDao()) val localSource = LocalSource(db.entriesDao(), db.transmittersDao())
val remoteSource = RemoteSource(ioDispatcher) val remoteSource = RemoteSource(ioDispatcher)
return DefaultRepository(dataParser, localSource, remoteSource) return DefaultRepository(dataParser, localSource, remoteSource, preferencesHandler)
}
@Provides
@Singleton
fun providePreferencesHandler(
locationManager: LocationManager,
sharedPreferences: SharedPreferences
): PreferencesHandler {
return PreferencesSource(locationManager, sharedPreferences)
} }
@Provides @Provides
@@ -84,14 +84,6 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
passesSettings.setOnClickListener { findNavController().navigate(R.id.nav_prefs) } passesSettings.setOnClickListener { findNavController().navigate(R.id.nav_prefs) }
passesFab.setOnClickListener { findNavController().navigate(R.id.nav_entries) } passesFab.setOnClickListener { findNavController().navigate(R.id.nav_entries) }
} }
// val appBarConfiguration = AppBarConfiguration(setOf(R.id.nav_passes))
// binding.passesToolbar.setupWithNavController(findNavController(), appBarConfiguration)
// binding.passesToolbar.navigationIcon =
// ResourcesCompat.getDrawable(resources, R.drawable.ic_add, requireActivity().theme)
// binding.passesToolbar.inflateMenu(R.menu.passes_menu)
// binding.passesFab.setOnClickListener {
// findNavController().navigate(R.id.nav_entries)
// }
passesViewModel.passes.observe(viewLifecycleOwner, { passesResult -> passesViewModel.passes.observe(viewLifecycleOwner, { passesResult ->
handleNewPasses(passesResult, passesAdapter, binding) handleNewPasses(passesResult, passesAdapter, binding)
}) })
@@ -28,7 +28,6 @@ import com.rtbishop.look4sat.domain.predict.SatPass
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.*
import java.util.*
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@@ -38,7 +37,7 @@ class PassesViewModel @Inject constructor(
private val preferences: PreferencesSource private val preferences: PreferencesSource
) : ViewModel() { ) : ViewModel() {
private val _passes = MutableLiveData<DataState<List<SatPass>>>(DataState.Loading) private val _passes = MutableLiveData<DataState<List<SatPass>>>()
private val _isFirstLaunchDone = MutableLiveData<Boolean>() private val _isFirstLaunchDone = MutableLiveData<Boolean>()
private var passesProcessing: Job? = null private var passesProcessing: Job? = null
val passes: LiveData<DataState<List<SatPass>>> = _passes val passes: LiveData<DataState<List<SatPass>>> = _passes
@@ -69,15 +68,15 @@ class PassesViewModel @Inject constructor(
fun triggerInitialSetup() { fun triggerInitialSetup() {
preferences.updatePositionFromGPS() preferences.updatePositionFromGPS()
viewModelScope.launch { viewModelScope.launch {
_passes.postValue(DataState.Loading) // _passes.postValue(DataState.Loading)
val satellites = dataRepository.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()
dataRepository.updateDataFromWeb() // dataRepository.updateDataFromWeb()
dataRepository.updateSelection(isSelected = true) // dataRepository.updateSelection(isSelected = true)
predictor.forceCalculation(satellites, stationPos, Date().time, hoursAhead, minElev) // predictor.forceCalculation(satellites, stationPos, Date().time, hoursAhead, minElev)
preferences.setSetupDone() // preferences.setSetupDone()
_isFirstLaunchDone.value = true _isFirstLaunchDone.value = true
} }
} }
@@ -23,22 +23,20 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.app.AppCompatDialogFragment import androidx.appcompat.app.AppCompatDialogFragment
import androidx.lifecycle.lifecycleScope
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.PreferencesHandler
import com.rtbishop.look4sat.databinding.DialogSourcesBinding import com.rtbishop.look4sat.databinding.DialogSourcesBinding
import com.rtbishop.look4sat.domain.DataRepository
import com.rtbishop.look4sat.framework.model.Source import com.rtbishop.look4sat.framework.model.Source
import com.rtbishop.look4sat.presentation.setNavResult import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class SourcesDialog : AppCompatDialogFragment() { class SourcesDialog : AppCompatDialogFragment() {
@Inject @Inject
lateinit var dataRepository: DataRepository lateinit var preferences: PreferencesHandler
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)
@@ -46,37 +44,14 @@ class SourcesDialog : AppCompatDialogFragment() {
override fun onViewCreated(view: View, state: Bundle?) { override fun onViewCreated(view: View, state: Bundle?) {
super.onViewCreated(view, state) super.onViewCreated(view, state)
// liveData { emit(dataRepository.getWebSources()) }.observe(viewLifecycleOwner, { sources -> val sources = preferences.loadDataSources()
// val sourcesAdapter = SourcesAdapter().apply { setSources(sources.map { Source(it) }) } val sourcesAdapter = SourcesAdapter().apply { setSources(sources.map { Source(it) }) }
// DialogSourcesBinding.bind(view).apply {
// dialog?.window?.setLayout(
// WindowManager.LayoutParams.MATCH_PARENT,
// WindowManager.LayoutParams.WRAP_CONTENT
// )
// sourcesRecycler.apply {
// adapter = sourcesAdapter
// layoutManager = LinearLayoutManager(requireContext())
// }
// sourcesBtnAdd.setOnClickListener {
// sourcesAdapter.addSource()
// }
// sourcesBtnPos.setOnClickListener {
// setNavResult("sources", sourcesAdapter.getSources().map { it.sourceUrl })
// dismiss()
// }
// sourcesBtnNeg.setOnClickListener { dismiss() }
// }
// })
lifecycleScope.launch {
val sources = dataRepository.getWebSources()
val sourcesAdapter = SourcesAdapter()
DialogSourcesBinding.bind(view).apply { DialogSourcesBinding.bind(view).apply {
dialog?.window?.setLayout( dialog?.window?.setLayout(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT WindowManager.LayoutParams.WRAP_CONTENT
) )
sourcesRecycler.apply { sourcesRecycler.apply {
sourcesAdapter.apply { setSources(sources.map { Source(it) }) }
adapter = sourcesAdapter adapter = sourcesAdapter
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
} }
@@ -90,6 +65,4 @@ class SourcesDialog : AppCompatDialogFragment() {
sourcesBtnNeg.setOnClickListener { dismiss() } sourcesBtnNeg.setOnClickListener { dismiss() }
} }
} }
}
} }
@@ -31,29 +31,16 @@ import kotlin.system.measureTimeMillis
class DefaultRepository( class DefaultRepository(
private val dataParser: DataParser, private val dataParser: DataParser,
private val localSource: LocalDataSource, private val localSource: LocalDataSource,
private val remoteSource: RemoteDataSource private val remoteSource: RemoteDataSource,
private val preferences: PreferencesHandler
) : DataRepository { ) : DataRepository {
override val defaultSelection = listOf(43700, 25544, 25338, 28654, 33591, 40069, 27607, 24278)
override val defaultSources = listOf(
"https://celestrak.com/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
"https://amsat.org/tle/current/nasabare.txt",
"https://www.prismnet.com/~mmccants/tles/classfd.zip",
"https://www.prismnet.com/~mmccants/tles/inttles.zip"
)
override val transmittersSource = "https://db.satnogs.org/api/transmitters/?format=json"
override fun getSatelliteItems() = localSource.getSatelliteItems() override fun getSatelliteItems() = localSource.getSatelliteItems()
override suspend fun getSelectedSatellites() = localSource.getSelectedSatellites() override suspend fun getSelectedSatellites() = localSource.getSelectedSatellites()
override suspend fun getTransmitters(catnum: Int) = localSource.getTransmitters(catnum) override suspend fun getTransmitters(catnum: Int) = localSource.getTransmitters(catnum)
override suspend fun getWebSources() = localSource.getSources().also { sources ->
return if (sources.isNotEmpty()) sources
else defaultSources
}
override suspend fun updateDataFromFile(stream: InputStream) { override suspend fun updateDataFromFile(stream: InputStream) {
localSource.updateEntries(importSatellites(stream)) localSource.updateEntries(importSatellites(stream))
} }
@@ -61,7 +48,7 @@ class DefaultRepository(
override suspend fun updateDataFromWeb(sources: List<String>) { override suspend fun updateDataFromWeb(sources: List<String>) {
coroutineScope { coroutineScope {
launch { launch {
localSource.updateSources(sources) preferences.saveDataSources(sources)
} }
launch { launch {
val updateTimeMillis = measureTimeMillis { val updateTimeMillis = measureTimeMillis {
@@ -91,7 +78,7 @@ class DefaultRepository(
println("Update from web took $updateTimeMillis ms") println("Update from web took $updateTimeMillis ms")
} }
launch { launch {
remoteSource.fetchFileStream(transmittersSource)?.let { inputStream -> remoteSource.fetchFileStream(preferences.transmittersSource)?.let { inputStream ->
val transmitters = dataParser.parseJSONStream(inputStream) val transmitters = dataParser.parseJSONStream(inputStream)
localSource.updateTransmitters(transmitters) localSource.updateTransmitters(transmitters)
} }
@@ -29,15 +29,11 @@ interface LocalDataSource {
suspend fun getSelectedSatellites(): List<Satellite> suspend fun getSelectedSatellites(): List<Satellite>
suspend fun getSources(): List<String>
suspend fun getTransmitters(catnum: Int): List<Transmitter> suspend fun getTransmitters(catnum: Int): List<Transmitter>
suspend fun updateEntries(entries: List<SatEntry>) suspend fun updateEntries(entries: List<SatEntry>)
suspend fun updateEntriesSelection(catnums: List<Int>, isSelected: Boolean) suspend fun updateEntriesSelection(catnums: List<Int>, isSelected: Boolean)
suspend fun updateSources(sources: List<String>)
suspend fun updateTransmitters(transmitters: List<Transmitter>) suspend fun updateTransmitters(transmitters: List<Transmitter>)
} }
@@ -0,0 +1,35 @@
/*
* 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.data
interface PreferencesHandler {
val defaultSources: List<String>
get() = listOf(
"https://celestrak.com/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
"https://amsat.org/tle/current/nasabare.txt",
"https://prismnet.com/~mmccants/tles/classfd.zip",
"https://prismnet.com/~mmccants/tles/inttles.zip"
)
val transmittersSource: String
get() = "https://db.satnogs.org/api/transmitters/?format=json"
fun loadDataSources(): List<String>
fun saveDataSources(sources: List<String>)
}
@@ -25,21 +25,15 @@ import java.io.InputStream
interface DataRepository { interface DataRepository {
val defaultSelection: List<Int>
val defaultSources: List<String>
val transmittersSource: String
fun getSatelliteItems(): Flow<List<SatItem>> fun getSatelliteItems(): Flow<List<SatItem>>
suspend fun getSelectedSatellites(): List<Satellite> suspend fun getSelectedSatellites(): List<Satellite>
suspend fun getTransmitters(catnum: Int): List<Transmitter> suspend fun getTransmitters(catnum: Int): List<Transmitter>
suspend fun getWebSources(): List<String>
suspend fun updateDataFromFile(stream: InputStream) suspend fun updateDataFromFile(stream: InputStream)
suspend fun updateDataFromWeb(sources: List<String> = defaultSources) suspend fun updateDataFromWeb(sources: List<String>)
suspend fun updateSelection(catnums: List<Int> = defaultSelection, isSelected: Boolean = true) suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean = true)
} }