Implemented Clean architecture, removed redundant dependencies

pull/65/head
Arty Bishop 2021-04-23 12:59:18 +01:00
rodzic 53fcf4790a
commit e9a6471b6d
66 zmienionych plików z 318 dodań i 934 usunięć

Wyświetl plik

@ -32,12 +32,6 @@ android {
}
}
}
configurations {
all {
exclude module: "commons-logging"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
@ -68,7 +62,6 @@ dependencies {
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
implementation "com.github.davidmoten:predict4java:$predict4java_version"
implementation "org.osmdroid:osmdroid-android:$osmdroid_version"
implementation "com.jakewharton.timber:timber:$timber_version"

Wyświetl plik

@ -0,0 +1,114 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "53fa623fc55b279c50d33e6263866a89",
"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 NOT NULL, 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": true
}
],
"primaryKey": {
"columnNames": [
"uuid"
],
"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, '53fa623fc55b279c50d33e6263866a89')"
]
}
}

Wyświetl plik

@ -1,35 +0,0 @@
/*
* 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.api
import com.rtbishop.look4sat.data.model.SatTrans
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Streaming
import retrofit2.http.Url
interface SatelliteService {
@Streaming
@GET
suspend fun fetchFileByUrl(@Url url: String): Response<ResponseBody>
@GET("transmitters/")
suspend fun fetchTransmitters(): List<SatTrans>
}

Wyświetl plik

@ -1,53 +0,0 @@
/*
* 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.database
import androidx.room.TypeConverter
import com.github.amsacode.predict4java.Satellite
import com.github.amsacode.predict4java.SatelliteFactory
import com.github.amsacode.predict4java.TLE
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
object RoomConverters {
private lateinit var tleAdapter: JsonAdapter<TLE>
fun initialize(moshi: Moshi) {
tleAdapter = moshi.adapter(TLE::class.java)
}
@JvmStatic
@TypeConverter
fun tleToString(tle: TLE): String {
return tleAdapter.toJson(tle)
}
@JvmStatic
@TypeConverter
fun tleFromString(string: String): TLE? {
return tleAdapter.fromJson(string)
}
@JvmStatic
@TypeConverter
fun satFromString(string: String): Satellite? {
val tle = tleAdapter.fromJson(string)
return SatelliteFactory.createSatellite(tle)
}
}

Wyświetl plik

@ -1,81 +0,0 @@
/*
* 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.database
import androidx.room.*
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.data.model.SatEntry
import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.data.model.SatTrans
import kotlinx.coroutines.flow.Flow
@Dao
interface SatelliteDao {
@Transaction
@Query("SELECT catNum, name, isSelected FROM entries ORDER BY name ASC")
fun getSatItems(): Flow<List<SatItem>>
@Query("SELECT * FROM transmitters WHERE catNum = :catNum")
fun getSatTransmitters(catNum: Int): Flow<List<SatTrans>>
@Query("SELECT tle FROM entries WHERE isSelected = 1")
suspend fun getSelectedSatellites(): List<Satellite>
@Query("SELECT catNum FROM entries WHERE isSelected = 1")
suspend fun getSelectedCatNums(): List<Int>
@Transaction
suspend fun updateEntries(entries: List<SatEntry>) {
val selectedCatNums = getSelectedCatNums()
insertEntries(entries)
restoreEntriesSelection(selectedCatNums, true)
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertEntries(entries: List<SatEntry>)
@Transaction
suspend fun restoreEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
clearEntriesSelection()
catNums.forEach { catNum -> updateEntrySelection(catNum, isSelected) }
}
@Query("UPDATE entries SET isSelected = 0")
suspend fun clearEntriesSelection()
@Query("UPDATE entries SET isSelected = :isSelected WHERE catNum = :catNum")
suspend fun updateEntrySelection(catNum: Int, isSelected: Boolean)
@Transaction
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
catNums.forEach { catNum -> updateEntrySelection(catNum, isSelected) }
}
@Transaction
suspend fun updateTransmitters(transmitters: List<SatTrans>) {
deleteTransmitters()
insertTransmitters(transmitters)
}
@Query("DELETE from transmitters")
suspend fun deleteTransmitters()
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertTransmitters(transmitters: List<SatTrans>)
}

Wyświetl plik

@ -1,48 +0,0 @@
/*
* 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.database
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.rtbishop.look4sat.data.model.SatEntry
import com.rtbishop.look4sat.data.model.SatTrans
@Database(entities = [SatEntry::class, SatTrans::class], version = 3)
@TypeConverters(RoomConverters::class)
abstract class SatelliteDb : RoomDatabase() {
abstract fun satelliteDao(): SatelliteDao
}
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE sources")
}
}
val MIGRATION_2_3 = object : Migration(2, 3) {
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 NOT NULL, PRIMARY KEY(uuid))")
database.execSQL("INSERT INTO trans_backup (uuid, info, isAlive, downlink, uplink, mode, isInverted, catNum) SELECT uuid, description, isAlive, downlinkLow, uplinkLow, mode, isInverted, catNum FROM transmitters")
database.execSQL("DROP TABLE transmitters")
database.execSQL("ALTER TABLE trans_backup RENAME TO transmitters")
}
}

Wyświetl plik

@ -1,24 +0,0 @@
/*
* 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.model
sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
object InProgress : Result<Nothing>()
}

Wyświetl plik

@ -1,30 +0,0 @@
/*
* 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.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.github.amsacode.predict4java.TLE
@Entity(tableName = "entries")
data class SatEntry(
val tle: TLE,
var isSelected: Boolean = false,
@PrimaryKey val catNum: Int = tle.catnum,
val name: String = tle.name
)

Wyświetl plik

@ -1,33 +0,0 @@
/*
* 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.model
import androidx.room.Relation
data class SatItem(
val catNum: Int,
val name: String,
var isSelected: Boolean,
@Relation(
parentColumn = "catNum",
entity = SatTrans::class,
entityColumn = "catNum",
projection = ["mode"]
)
val modes: List<String>
)

Wyświetl plik

@ -1,46 +0,0 @@
/*
* 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.model
import com.rtbishop.look4sat.utility.PassPredictor
import java.util.*
data class SatPass(
val catNum: Int,
val name: String,
val isDeepSpace: Boolean,
private val aosTime: Long,
val aosAzimuth: Double,
private val losTime: Long,
val losAzimuth: Double,
private val tcaTime: Long,
val tcaAzimuth: Double,
val altitude: Double,
val maxElevation: Double,
val predictor: PassPredictor,
var progress: Int = 0
) {
val aosDate: Date
get() = Date(aosTime)
val losDate: Date
get() = Date(losTime)
val tcaDate: Date
get() = Date(tcaTime)
}

Wyświetl plik

@ -1,34 +0,0 @@
/*
* 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.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.squareup.moshi.Json
@Entity(tableName = "transmitters")
data class SatTrans(
@PrimaryKey @field:Json(name = "uuid") val uuid: String,
@field:Json(name = "description") val info: String,
@field:Json(name = "alive") val isAlive: Boolean,
@field:Json(name = "downlink_low") var downlink: Long?,
@field:Json(name = "uplink_low") var uplink: Long?,
@field:Json(name = "mode") val mode: String?,
@field:Json(name = "invert") val isInverted: Boolean,
@field:Json(name = "norad_cat_id") val catNum: Int
)

Wyświetl plik

@ -1,35 +0,0 @@
/*
* 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.model
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.Satellite
import org.osmdroid.views.overlay.Overlay
data class SelectedSat(
val pass: Satellite,
val catNum: Int,
val name: String,
val range: Double,
val altitude: Double,
val velocity: Double,
val qthLoc: String,
val osmPos: Position,
val footprint: Overlay,
val groundTrack: Overlay
)

Wyświetl plik

@ -1,20 +0,0 @@
/*
* 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.model
class TleSource(var url: String = String())

Wyświetl plik

@ -1,127 +0,0 @@
/*
* 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.repository
import android.content.ContentResolver
import android.net.Uri
import com.github.amsacode.predict4java.Satellite
import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.data.api.SatelliteService
import com.rtbishop.look4sat.data.database.SatelliteDao
import com.rtbishop.look4sat.data.model.SatEntry
import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.data.model.SatTrans
import com.rtbishop.look4sat.data.model.TleSource
import com.rtbishop.look4sat.di.IoDispatcher
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.util.zip.ZipInputStream
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SatelliteRepo @Inject constructor(
private val resolver: ContentResolver,
private val satelliteDao: SatelliteDao,
private val satelliteService: SatelliteService,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) {
fun getSatItems(): Flow<List<SatItem>> {
return satelliteDao.getSatItems()
}
fun getSatTransmitters(catNum: Int): Flow<List<SatTrans>> {
return satelliteDao.getSatTransmitters(catNum)
}
suspend fun getSelectedSatellites(): List<Satellite> {
return satelliteDao.getSelectedSatellites()
}
suspend fun importSatDataFromFile(uri: Uri) {
runCatching {
resolver.openInputStream(uri)?.use { stream ->
val entries = importEntriesFromStreams(listOf(stream))
satelliteDao.updateEntries(entries)
}
}.onFailure { throw it }
}
suspend fun importSatDataFromWeb(sources: List<TleSource>) {
coroutineScope {
launch { importEntriesFromSources(sources) }
launch { importTransmitters() }
}
}
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
satelliteDao.updateEntriesSelection(catNums, isSelected)
}
private suspend fun importEntriesFromSources(sources: List<TleSource>) {
val streams = getStreamsForSources(sources)
val entries = importEntriesFromStreams(streams)
satelliteDao.updateEntries(entries)
}
private suspend fun importTransmitters() {
val transmitters = satelliteService.fetchTransmitters().filter { it.isAlive }
satelliteDao.updateTransmitters(transmitters)
}
private suspend fun getStreamsForSources(sources: List<TleSource>): List<InputStream> {
val streams = mutableListOf<InputStream>()
sources.forEach { tleSource ->
satelliteService.fetchFileByUrl(tleSource.url).body()?.byteStream()?.let { stream ->
if (tleSource.url.contains(".zip", true)) {
streams.add(getZipStream(stream))
} else {
streams.add(stream)
}
}
}
return streams
}
private suspend fun getZipStream(inputStream: InputStream): InputStream {
var stream: InputStream
withContext(ioDispatcher) {
ZipInputStream(inputStream).apply {
nextEntry
stream = this
}
}
return stream
}
private suspend fun importEntriesFromStreams(streams: List<InputStream>): List<SatEntry> {
val importedEntries = mutableListOf<SatEntry>()
withContext(ioDispatcher) {
streams.forEach { stream ->
val entries = TLE.importSat(stream).map { tle -> SatEntry(tle) }
importedEntries.addAll(entries)
}
}
return importedEntries
}
}

Wyświetl plik

@ -0,0 +1,43 @@
package com.rtbishop.look4sat.di
import com.rtbishop.look4sat.domain.SatelliteRepo
import com.rtbishop.look4sat.interactors.*
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
object InteractorsModule {
@Provides
fun provideGetSatItemsUseCase(satelliteRepo: SatelliteRepo): GetSatItems {
return GetSatItems(satelliteRepo)
}
@Provides
fun provideGetSelectedSatellitesUseCase(satelliteRepo: SatelliteRepo): GetSelectedSatellites {
return GetSelectedSatellites(satelliteRepo)
}
@Provides
fun provideGetTransmittersForSatUseCase(satelliteRepo: SatelliteRepo): GetTransmittersForSat {
return GetTransmittersForSat(satelliteRepo)
}
@Provides
fun provideImportDataFromFileUseCase(satelliteRepo: SatelliteRepo): ImportDataFromFile {
return ImportDataFromFile(satelliteRepo)
}
@Provides
fun provideImportDataFromWebUseCase(satelliteRepo: SatelliteRepo): ImportDataFromWeb {
return ImportDataFromWeb(satelliteRepo)
}
@Provides
fun provideUpdateEntriesSelectionUseCase(satelliteRepo: SatelliteRepo): UpdateEntriesSelection {
return UpdateEntriesSelection(satelliteRepo)
}
}

Wyświetl plik

@ -19,29 +19,55 @@ package com.rtbishop.look4sat.di
import android.content.Context
import androidx.room.Room
import com.rtbishop.look4sat.data.database.*
import com.rtbishop.look4sat.data.DefaultSatelliteRepo
import com.rtbishop.look4sat.data.LocalDataSource
import com.rtbishop.look4sat.data.RemoteDataSource
import com.rtbishop.look4sat.domain.SatelliteRepo
import com.rtbishop.look4sat.framework.db.RoomConverters
import com.rtbishop.look4sat.framework.db.RoomDataSource
import com.rtbishop.look4sat.framework.db.SatelliteDao
import com.rtbishop.look4sat.framework.db.SatelliteDb
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
object LocalSourceModule {
@Provides
fun provideLocalDataSource(satelliteDao: SatelliteDao): LocalDataSource {
return RoomDataSource(satelliteDao)
}
@Provides
@Singleton
fun provideDefaultRepository(
localSource: LocalDataSource,
remoteSource: RemoteDataSource,
@IoDispatcher ioDispatcher: CoroutineDispatcher
): SatelliteRepo {
return DefaultSatelliteRepo(localSource, remoteSource, ioDispatcher)
}
@Provides
fun provideMoshi(): Moshi {
return Moshi.Builder().build()
}
@Provides
fun provideSatDataDao(db: SatelliteDb): SatelliteDao {
return db.satelliteDao()
}
@Provides
fun provideSatelliteDb(@ApplicationContext context: Context, moshi: Moshi): SatelliteDb {
RoomConverters.initialize(moshi)
return Room.databaseBuilder(context, SatelliteDb::class.java, "satDb")
.addMigrations(MIGRATION_1_2)
.addMigrations(MIGRATION_2_3)
.build()
return Room.databaseBuilder(context, SatelliteDb::class.java, "SatelliteDb").build()
}
}

Wyświetl plik

@ -17,8 +17,9 @@
*/
package com.rtbishop.look4sat.di
import com.rtbishop.look4sat.data.api.SatelliteService
import com.squareup.moshi.Moshi
import com.rtbishop.look4sat.data.RemoteDataSource
import com.rtbishop.look4sat.framework.api.NetworkDataSource
import com.rtbishop.look4sat.framework.api.SatelliteService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -28,13 +29,13 @@ import retrofit2.converter.moshi.MoshiConverterFactory
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
object RemoteSourceModule {
@Provides
fun provideMoshi(): Moshi {
return Moshi.Builder().build()
fun provideRemoteDataSource(satelliteService: SatelliteService): RemoteDataSource {
return NetworkDataSource(satelliteService)
}
@Provides
fun provideSatDataService(): SatelliteService {
return Retrofit.Builder()

Wyświetl plik

@ -4,14 +4,14 @@ import com.rtbishop.look4sat.data.RemoteDataSource
import com.rtbishop.look4sat.domain.model.SatTrans
import java.io.InputStream
class RetrofitDataSource(private val satelliteService: SatelliteService) : RemoteDataSource {
class NetworkDataSource(private val api: SatelliteService) : RemoteDataSource {
override suspend fun fetchFileStream(url: String): InputStream? {
return satelliteService.fetchFileByUrl(url).body()?.byteStream()
return api.fetchFileByUrl(url).body()?.byteStream()
}
override suspend fun fetchTransmitters(): List<SatTrans> {
return satelliteService.fetchTransmitters().map { trans ->
return api.fetchTransmitters().map { trans ->
SatTrans(
trans.uuid, trans.info, trans.isAlive, trans.downlink,
trans.uplink, trans.mode, trans.isInverted, trans.catNum

Wyświetl plik

@ -18,8 +18,8 @@
package com.rtbishop.look4sat.framework.db
import androidx.room.TypeConverter
import com.rtbishop.look4sat.predict4kotlin.Satellite
import com.rtbishop.look4sat.predict4kotlin.TLE
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.TLE
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi

Wyświetl plik

@ -4,7 +4,7 @@ import com.rtbishop.look4sat.data.LocalDataSource
import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.SatTrans
import com.rtbishop.look4sat.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import com.rtbishop.look4sat.utility.DataMapper
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@ -38,4 +38,4 @@ class RoomDataSource(private val satelliteDao: SatelliteDao) : LocalDataSource {
val satTransList = DataMapper.domainTransListToSatTransList(satelliteTrans)
satelliteDao.updateTransmitters(satTransList)
}
}
}

Wyświetl plik

@ -21,7 +21,7 @@ import androidx.room.*
import com.rtbishop.look4sat.framework.model.SatEntry
import com.rtbishop.look4sat.framework.model.SatItem
import com.rtbishop.look4sat.framework.model.SatTrans
import com.rtbishop.look4sat.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import kotlinx.coroutines.flow.Flow
@Dao

Wyświetl plik

@ -19,7 +19,7 @@ package com.rtbishop.look4sat.framework.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.rtbishop.look4sat.predict4kotlin.TLE
import com.rtbishop.look4sat.domain.predict4kotlin.TLE
@Entity(tableName = "entries")
data class SatEntry(

Wyświetl plik

@ -1,46 +0,0 @@
/*
* 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.model
import com.rtbishop.look4sat.utility.PassPredictor
import java.util.*
data class SatPass(
val catNum: Int,
val name: String,
val isDeepSpace: Boolean,
private val aosTime: Long,
val aosAzimuth: Double,
private val losTime: Long,
val losAzimuth: Double,
private val tcaTime: Long,
val tcaAzimuth: Double,
val altitude: Double,
val maxElevation: Double,
val predictor: PassPredictor,
var progress: Int = 0
) {
val aosDate: Date
get() = Date(aosTime)
val losDate: Date
get() = Date(losTime)
val tcaDate: Date
get() = Date(tcaTime)
}

Wyświetl plik

@ -17,8 +17,8 @@
*/
package com.rtbishop.look4sat.framework.model
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import org.osmdroid.views.overlay.Overlay
data class SelectedSat(

Wyświetl plik

@ -22,8 +22,8 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.databinding.SatItemBinding
import com.rtbishop.look4sat.domain.model.SatItem
class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {

Wyświetl plik

@ -27,10 +27,10 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.snackbar.Snackbar
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.data.model.TleSource
import com.rtbishop.look4sat.framework.model.Result
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.framework.model.TleSource
import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.getNavResult
import com.rtbishop.look4sat.utility.navigateSafe

Wyświetl plik

@ -17,6 +17,7 @@
*/
package com.rtbishop.look4sat.presentation.entriesScreen
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
import android.widget.SearchView
@ -24,10 +25,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.data.model.TleSource
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.framework.model.Result
import com.rtbishop.look4sat.framework.model.TleSource
import com.rtbishop.look4sat.interactors.GetSatItems
import com.rtbishop.look4sat.interactors.ImportDataFromFile
import com.rtbishop.look4sat.interactors.ImportDataFromWeb
import com.rtbishop.look4sat.interactors.UpdateEntriesSelection
import com.rtbishop.look4sat.utility.PrefsManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collect
@ -40,13 +44,17 @@ import kotlin.system.measureTimeMillis
@HiltViewModel
class EntriesViewModel @Inject constructor(
private val prefsManager: PrefsManager,
private val satelliteRepo: SatelliteRepo
private val resolver: ContentResolver,
private val getSatItems: GetSatItems,
private val importDataFromFile: ImportDataFromFile,
private val importDataFromWeb: ImportDataFromWeb,
private val updateEntriesSelection: UpdateEntriesSelection,
) : ViewModel(), EntriesAdapter.EntriesClickListener, SearchView.OnQueryTextListener {
private val transModes = MutableLiveData(prefsManager.loadModesSelection())
private val currentQuery = MutableLiveData(String())
private val itemsWithModes = transModes.switchMap { modes ->
liveData { satelliteRepo.getSatItems().collect { emit(filterByModes(it, modes)) } }
liveData { getSatItems().collect { emit(filterByModes(it, modes)) } }
}
private val itemsWithQuery = currentQuery.switchMap { query ->
itemsWithModes.map { items -> Result.Success(filterByQuery(items, query)) }
@ -60,11 +68,11 @@ class EntriesViewModel @Inject constructor(
fun importSatDataFromFile(uri: Uri) {
viewModelScope.launch {
_satData.value = Result.InProgress
try {
satelliteRepo.importSatDataFromFile(uri)
} catch (exception: Exception) {
_satData.value = Result.Error(exception)
}
runCatching {
resolver.openInputStream(uri)?.use { stream ->
importDataFromFile(stream)
}
}.onFailure { _satData.value = Result.Error(it) }
}
}
@ -76,9 +84,10 @@ class EntriesViewModel @Inject constructor(
val updateMillis = measureTimeMillis {
try {
prefsManager.saveTleSources(satSources)
satelliteRepo.importSatDataFromWeb(satSources)
importDataFromWeb(satSources.map { it.url })
} catch (exception: Exception) {
_satData.value = Result.Error(exception)
Timber.d(exception)
}
}
Timber.d("Update from WEB took $updateMillis ms")
@ -133,7 +142,7 @@ class EntriesViewModel @Inject constructor(
}
override fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
viewModelScope.launch { satelliteRepo.updateEntriesSelection(catNums, isSelected) }
viewModelScope.launch { updateEntriesSelection(catNums, isSelected) }
}
private fun filterByModes(items: List<SatItem>, modes: List<String>): List<SatItem> {

Wyświetl plik

@ -25,11 +25,11 @@ import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SelectedSat
import com.rtbishop.look4sat.databinding.FragmentMapBinding
import com.rtbishop.look4sat.framework.model.SelectedSat
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import com.rtbishop.look4sat.utility.PrefsManager
import dagger.hilt.android.AndroidEntryPoint
import org.osmdroid.config.Configuration

Wyświetl plik

@ -20,11 +20,11 @@ package com.rtbishop.look4sat.presentation.mapScreen
import android.graphics.Color
import android.graphics.Paint
import androidx.lifecycle.*
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.SatPos
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.data.model.SelectedSat
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.framework.model.SelectedSat
import com.rtbishop.look4sat.interactors.GetSelectedSatellites
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.SatPos
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import com.rtbishop.look4sat.utility.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
@ -39,9 +39,9 @@ import javax.inject.Inject
@HiltViewModel
class MapViewModel @Inject constructor(
prefsManager: PrefsManager,
private val satelliteRepo: SatelliteRepo,
private val qthConverter: QthConverter
private val getSelectedSatellites: GetSelectedSatellites,
private val qthConverter: QthConverter,
prefsManager: PrefsManager
) : ViewModel() {
private val dateNow = Date()
@ -71,12 +71,12 @@ class MapViewModel @Inject constructor(
fun getSatMarkers(): LiveData<Map<Satellite, Position>> = _satMarkers
val stationPosition = liveData {
emit(Position(gsp.latitude.toOsmLat(), gsp.longitude.toOsmLon()))
emit(Position(gsp.lat.toOsmLat(), gsp.lon.toOsmLon()))
}
init {
viewModelScope.launch {
val satellites = satelliteRepo.getSelectedSatellites()
val satellites = getSelectedSatellites()
if (satellites.isNotEmpty()) {
filteredSats = satellites
selectedSat = satellites.first()
@ -171,18 +171,18 @@ class MapViewModel @Inject constructor(
}
private fun getSatFootprint(satPos: SatPos): Overlay {
val rangePoints = mutableListOf<GeoPoint>()
satPos.rangeCircle.forEach {
val points = mutableListOf<GeoPoint>()
satPos.getRangeCircle().forEach {
val osmPos = Position(it.lat.toOsmLat(), it.lon.toOsmLon())
rangePoints.add(GeoPoint(osmPos.lat, osmPos.lon))
points.add(GeoPoint(osmPos.lat, osmPos.lon))
}
return Polygon().apply {
fillPaint.set(footprintPaint)
outlinePaint.set(footprintPaint)
try {
points = rangePoints
this.points = points
} catch (e: IllegalArgumentException) {
Timber.d("RangeCircle: ${satPos.rangeCircle}, RangePoints: $rangePoints")
Timber.d("RangeCircle: ${satPos.getRangeCircle()}, RangePoints: $points")
}
}
}

Wyświetl plik

@ -23,9 +23,9 @@ import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.databinding.ItemPassGeoBinding
import com.rtbishop.look4sat.databinding.ItemPassLeoBinding
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
import java.text.SimpleDateFormat
import java.util.*

Wyświetl plik

@ -26,9 +26,9 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.framework.model.Result
import com.rtbishop.look4sat.databinding.FragmentPassesBinding
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.formatForTimer
import com.rtbishop.look4sat.utility.navigateSafe

Wyświetl plik

@ -21,10 +21,10 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.framework.model.Result
import com.rtbishop.look4sat.utility.PassesRepo
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.interactors.GetSelectedSatellites
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
import com.rtbishop.look4sat.utility.PrefsManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
@ -33,9 +33,9 @@ import javax.inject.Inject
@HiltViewModel
class PassesViewModel @Inject constructor(
private val prefsManager: PrefsManager,
private val satelliteRepo: SatelliteRepo,
private val passesRepo: PassesRepo
private val getSelectedSatellites: GetSelectedSatellites,
private val passesRepo: PassesRepo,
private val prefsManager: PrefsManager
) : ViewModel() {
private val _passes = MutableLiveData<Result<List<SatPass>>>(Result.InProgress)
@ -45,7 +45,7 @@ class PassesViewModel @Inject constructor(
init {
viewModelScope.launch {
_passes.postValue(Result.InProgress)
passesRepo.triggerCalculation(satelliteRepo.getSelectedSatellites())
passesRepo.triggerCalculation(getSelectedSatellites())
}
viewModelScope.launch {
passesRepo.passes.collect { passes ->
@ -59,7 +59,7 @@ class PassesViewModel @Inject constructor(
viewModelScope.launch {
_passes.postValue(Result.InProgress)
passesProcessing?.cancelAndJoin()
passesRepo.forceCalculation(satelliteRepo.getSelectedSatellites())
passesRepo.forceCalculation(getSelectedSatellites())
}
}

Wyświetl plik

@ -25,8 +25,8 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.databinding.FragmentPolarBinding
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.formatForTimer
import com.rtbishop.look4sat.utility.navigateSafe

Wyświetl plik

@ -25,7 +25,7 @@ import android.graphics.Path
import android.view.View
import androidx.core.content.ContextCompat
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
import java.util.*
import kotlin.math.cos
import kotlin.math.sin

Wyświetl plik

@ -18,11 +18,11 @@
package com.rtbishop.look4sat.presentation.polarScreen
import androidx.lifecycle.*
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.data.model.SatTrans
import com.rtbishop.look4sat.utility.PassesRepo
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.di.IoDispatcher
import com.rtbishop.look4sat.domain.model.SatTrans
import com.rtbishop.look4sat.interactors.GetTransmittersForSat
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
import com.rtbishop.look4sat.utility.OrientationProvider
import com.rtbishop.look4sat.utility.PrefsManager
import com.rtbishop.look4sat.utility.round
@ -39,7 +39,7 @@ class PolarViewModel @Inject constructor(
private val orientationProvider: OrientationProvider,
private val prefsManager: PrefsManager,
private val passesRepo: PassesRepo,
private val satelliteRepo: SatelliteRepo,
private val getTransmittersForSat: GetTransmittersForSat,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : ViewModel(), OrientationProvider.OrientationListener {
@ -98,7 +98,7 @@ class PolarViewModel @Inject constructor(
private fun processTransmitters(satPass: SatPass) {
viewModelScope.launch {
satelliteRepo.getSatTransmitters(satPass.catNum).collect { transList ->
getTransmittersForSat(satPass.catNum).collect { transList ->
while (isActive) {
val timeNow = Date()
val copiedList = transList.map { it.copy() }

Wyświetl plik

@ -23,8 +23,8 @@ import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SatTrans
import com.rtbishop.look4sat.databinding.ItemTransBinding
import com.rtbishop.look4sat.domain.model.SatTrans
import java.util.*
class TransAdapter : RecyclerView.Adapter<TransAdapter.TransHolder>() {

Wyświetl plik

@ -103,7 +103,7 @@ class PrefsFragment : PreferenceFragmentCompat() {
private fun setPositionFromQth(qthString: String): Boolean {
qthConverter.qthToLocation(qthString)?.let { gsp ->
prefsManager.setStationPosition(gsp.latitude, gsp.longitude, gsp.heightAMSL)
prefsManager.setStationPosition(gsp.lat, gsp.lon, gsp.alt)
showSnack(getString(R.string.pref_pos_success))
return true
}

Wyświetl plik

@ -21,8 +21,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.widget.doOnTextChanged
import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.data.model.TleSource
import com.rtbishop.look4sat.databinding.ItemTleSourceBinding
import com.rtbishop.look4sat.framework.model.TleSource
class SourcesAdapter(private val sources: MutableList<TleSource> = mutableListOf()) :
RecyclerView.Adapter<SourcesAdapter.TleSourceHolder>() {

Wyświetl plik

@ -26,8 +26,6 @@ import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.findNavController
import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.Satellite
import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.math.min
@ -64,10 +62,6 @@ fun Double.getOrbitalVelocity(): Double {
return sqrt(earthG * earthM / radius) / 1000
}
fun Satellite.getPredictor(stationPosition: GroundStationPosition): PassPredictor {
return PassPredictor(this, stationPosition)
}
fun NavController.navigateSafe(
@IdRes resId: Int,
args: Bundle? = null,

Wyświetl plik

@ -1,184 +0,0 @@
/*
* 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.utility
import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.SatPos
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.data.model.SatPass
import java.util.*
class PassPredictor(private val satellite: Satellite, private val qth: GroundStationPosition) {
private val oneQuarterOrbitMin = (24.0 * 60.0 / satellite.tle.meanmo / 4.0).toInt()
private val speedOfLight = 2.99792458E8
fun getDownlinkFreq(freq: Long, date: Date): Long {
val rangeRate = getSatPos(date).rangeRate
return (freq.toDouble() * (speedOfLight - rangeRate * 1000.0) / speedOfLight).toLong()
}
fun getUplinkFreq(freq: Long, date: Date): Long {
val rangeRate = getSatPos(date).rangeRate
return (freq.toDouble() * (speedOfLight + rangeRate * 1000.0) / speedOfLight).toLong()
}
fun getSatPos(date: Date): SatPos {
return satellite.getPosition(qth, date)
}
fun getPositions(refDate: Date, stepSec: Int, minBefore: Int, orbits: Double): List<SatPos> {
val positions = mutableListOf<SatPos>()
val orbitalPeriod = 24 * 60 / satellite.tle.meanmo
val endDate = Date(refDate.time + (orbitalPeriod * orbits * 60L * 1000L).toLong())
val startDate = Date(refDate.time - minBefore * 60L * 1000L)
var currentDate = startDate
while (currentDate.before(endDate)) {
positions.add(getSatPos(currentDate))
currentDate = Date(currentDate.time + stepSec * 1000)
}
return positions
}
fun getPasses(refDate: Date, hoursAhead: Int, windBack: Boolean): List<SatPass> {
val passes = mutableListOf<SatPass>()
val endDate = Date(refDate.time + hoursAhead * 60L * 60L * 1000L)
var startDate = refDate
var shouldWindBack = windBack
var lastAosDate: Date
var count = 0
if (satellite.willBeSeen(qth)) {
if (satellite.tle.isDeepspace) {
passes.add(nextDeepSpacePass(refDate))
} else {
do {
if (count > 0) shouldWindBack = false
val pass = nextNearEarthPass(startDate, shouldWindBack)
lastAosDate = pass.aosDate
passes.add(pass)
startDate =
Date(pass.losDate.time + (oneQuarterOrbitMin * 3) * 60L * 1000L)
count++
} while (lastAosDate < endDate)
}
}
return passes
}
private fun nextDeepSpacePass(refDate: Date): SatPass {
val satPos = getSatPos(refDate)
val id = satellite.tle.catnum
val name = satellite.tle.name
val isDeep = satellite.tle.isDeepspace
val aos = Date(refDate.time - 24 * 60L * 60L * 1000L).time
val los = Date(refDate.time + 24 * 60L * 60L * 1000L).time
val tca = Date((aos + los) / 2).time
val az = Math.toDegrees(satPos.azimuth)
val elev = Math.toDegrees(satPos.elevation)
val alt = satPos.altitude
return SatPass(id, name, isDeep, aos, az, los, az, tca, az, alt, elev, this)
}
private fun nextNearEarthPass(refDate: Date, windBack: Boolean = false): SatPass {
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply {
clear()
timeInMillis = refDate.time
}
val id = satellite.tle.catnum
val name = satellite.tle.name
val isDeep = satellite.tle.isDeepspace
var elevation: Double
var maxElevation = 0.0
var alt = 0.0
var tcaAz = 0.0
// wind back time 1/4 of an orbit
if (windBack) calendar.add(Calendar.MINUTE, -oneQuarterOrbitMin)
var satPos = getSatPos(calendar.time)
if (satPos.elevation > 0.0) {
// move forward in 30 second intervals until the sat goes below the horizon
do {
calendar.add(Calendar.SECOND, 30)
satPos = getSatPos(calendar.time)
} while (satPos.elevation > 0.0)
// move forward 3/4 of an orbit
calendar.add(Calendar.MINUTE, oneQuarterOrbitMin * 3)
}
// find the next time sat comes above the horizon
do {
calendar.add(Calendar.SECOND, 60)
satPos = getSatPos(calendar.time)
elevation = satPos.elevation
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = Math.toDegrees(satPos.azimuth)
}
} while (satPos.elevation < 0.0)
// refine to 3 seconds
calendar.add(Calendar.SECOND, -60)
do {
calendar.add(Calendar.SECOND, 3)
satPos = getSatPos(calendar.time)
elevation = satPos.elevation
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = Math.toDegrees(satPos.azimuth)
}
} while (satPos.elevation < 0.0)
val aos = satPos.time.time
val aosAz = Math.toDegrees(satPos.azimuth)
// find when sat goes below
do {
calendar.add(Calendar.SECOND, 30)
satPos = getSatPos(calendar.time)
elevation = satPos.elevation
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = Math.toDegrees(satPos.azimuth)
}
} while (satPos.elevation > 0.0)
// refine to 3 seconds
calendar.add(Calendar.SECOND, -30)
do {
calendar.add(Calendar.SECOND, 3)
satPos = getSatPos(calendar.time)
elevation = satPos.elevation
if (elevation > maxElevation) {
maxElevation = elevation
alt = satPos.altitude
tcaAz = Math.toDegrees(satPos.azimuth)
}
} while (satPos.elevation > 0.0)
val los = satPos.time.time
val losAz = Math.toDegrees(satPos.azimuth)
val tca = Date((aos + los) / 2).time
val elev = Math.toDegrees(maxElevation)
return SatPass(id, name, isDeep, aos, aosAz, los, losAz, tca, tcaAz, alt, elev, this)
}
}

Wyświetl plik

@ -17,9 +17,9 @@
*/
package com.rtbishop.look4sat.utility
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.di.DefaultDispatcher
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow

Wyświetl plik

@ -20,8 +20,8 @@ package com.rtbishop.look4sat.utility
import android.content.SharedPreferences
import android.hardware.GeomagneticField
import androidx.core.content.edit
import com.github.amsacode.predict4java.GroundStationPosition
import com.rtbishop.look4sat.data.model.TleSource
import com.rtbishop.look4sat.framework.model.TleSource
import com.rtbishop.look4sat.domain.predict4kotlin.GroundPos
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import javax.inject.Inject
@ -60,12 +60,12 @@ class PrefsManager @Inject constructor(val preferences: SharedPreferences, moshi
return preferences.getInt(keyMinElevation, 16).toDouble()
}
fun getStationPosition(): GroundStationPosition {
fun getStationPosition(): GroundPos {
val defaultGSP = "0.0"
val latitude = preferences.getString(keyLatitude, null) ?: defaultGSP
val longitude = preferences.getString(keyLongitude, null) ?: defaultGSP
val altitude = preferences.getString(keyAltitude, null) ?: defaultGSP
return GroundStationPosition(latitude.toDouble(), longitude.toDouble(), altitude.toDouble())
return GroundPos(latitude.toDouble(), longitude.toDouble(), altitude.toDouble())
}
fun setStationPosition(latitude: Double, longitude: Double, altitude: Double) {
@ -78,9 +78,9 @@ class PrefsManager @Inject constructor(val preferences: SharedPreferences, moshi
fun getMagDeclination(): Float {
val stationPosition = getStationPosition()
val lat = stationPosition.latitude.toFloat()
val lon = stationPosition.longitude.toFloat()
val alt = stationPosition.heightAMSL.toFloat()
val lat = stationPosition.lat.toFloat()
val lon = stationPosition.lon.toFloat()
val alt = stationPosition.alt.toFloat()
return GeomagneticField(lat, lon, alt, System.currentTimeMillis()).declination
}

Wyświetl plik

@ -17,12 +17,12 @@
*/
package com.rtbishop.look4sat.utility
import com.github.amsacode.predict4java.GroundStationPosition
import com.rtbishop.look4sat.domain.predict4kotlin.GroundPos
import javax.inject.Inject
class QthConverter @Inject constructor() {
fun qthToLocation(qthString: String): GroundStationPosition? {
fun qthToLocation(qthString: String): GroundPos? {
val trimmedQth = qthString.take(6)
if (!isValidQTH(trimmedQth)) return null
val lonFirst = (trimmedQth[0].toUpperCase().toInt() - 65) * 20
@ -33,7 +33,7 @@ class QthConverter @Inject constructor() {
val latThird = (((trimmedQth[5].toLowerCase().toInt() - 97) / 24.0) + (1.0 / 48.0)) - 90
val longitude = (lonFirst + lonSecond + lonThird).round(4)
val latitude = (latFirst + latSecond + latThird).round(4)
return GroundStationPosition(latitude, longitude, 0.0)
return GroundPos(latitude, longitude, 0.0)
}
fun locationToQTH(lat: Double, lon: Double): String? {

Wyświetl plik

@ -2,7 +2,7 @@ buildscript {
ext {
gradle_version = '4.1.3'
kotlin_version = '1.4.32'
coroutines_version = '1.4.1'
coroutines_version = '1.4.3'
material_version = '1.3.0'
constraint_layout_version = '2.0.4'
lifecycle_version = '2.3.1'
@ -11,7 +11,6 @@ buildscript {
room_version = '2.3.0'
hilt_version = '2.33-beta'
retrofit_version = '2.9.0'
predict4java_version = '1.3.1'
osmdroid_version = '6.1.10'
timber_version = '4.7.1'
junit_version = '4.13.2'

Wyświetl plik

@ -11,4 +11,7 @@ java {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
}
testImplementation "junit:junit:$junit_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
}

Wyświetl plik

@ -4,7 +4,7 @@ import com.rtbishop.look4sat.domain.SatelliteRepo
import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.SatTrans
import com.rtbishop.look4sat.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
@ -32,15 +32,13 @@ class DefaultSatelliteRepo(
}
override suspend fun importDataFromFile(stream: InputStream) = withContext(ioDispatcher) {
val entries = mutableListOf<SatEntry>()
val tleList = Satellite.importTLE(stream)
tleList.forEach { tle -> entries.add(SatEntry(tle)) }
val entries = Satellite.importTLE(stream).map { tle -> SatEntry(tle) }
localSource.updateEntries(entries)
}
override suspend fun importDataFromWeb(sources: List<String>) {
coroutineScope {
launch {
launch(ioDispatcher) {
val entries = mutableListOf<SatEntry>()
val streams = mutableListOf<InputStream>()
sources.forEach { source ->
@ -59,7 +57,7 @@ class DefaultSatelliteRepo(
}
localSource.updateEntries(entries)
}
launch {
launch(ioDispatcher) {
val transmitters = remoteSource.fetchTransmitters().filter { it.isAlive }
localSource.updateTransmitters(transmitters)
}

Wyświetl plik

@ -3,7 +3,7 @@ package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.model.SatEntry
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.SatTrans
import com.rtbishop.look4sat.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import kotlinx.coroutines.flow.Flow
interface LocalDataSource {

Wyświetl plik

@ -2,7 +2,7 @@ package com.rtbishop.look4sat.domain
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.SatTrans
import com.rtbishop.look4sat.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
import kotlinx.coroutines.flow.Flow
import java.io.InputStream
@ -19,4 +19,4 @@ interface SatelliteRepo {
suspend fun importDataFromWeb(sources: List<String>)
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean)
}
}

Wyświetl plik

@ -1,6 +1,6 @@
package com.rtbishop.look4sat.domain.model
import com.rtbishop.look4sat.predict4kotlin.TLE
import com.rtbishop.look4sat.domain.predict4kotlin.TLE
data class SatEntry(
val tle: TLE,

Wyświetl plik

@ -15,7 +15,7 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
import kotlin.math.*

Wyświetl plik

@ -15,6 +15,6 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
data class GroundPos(val lat: Double, val lon: Double, val alt: Double, val name: String = "base")

Wyświetl plik

@ -15,7 +15,7 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
import kotlin.math.*

Wyświetl plik

@ -15,7 +15,7 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
import java.util.*

Wyświetl plik

@ -15,6 +15,6 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
data class Position(val lat: Double, val lon: Double)

Wyświetl plik

@ -15,7 +15,7 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
import java.util.*

Wyświetl plik

@ -15,7 +15,7 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
import java.util.*
import kotlin.math.*

Wyświetl plik

@ -15,7 +15,7 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
import java.io.InputStream
import java.util.*

Wyświetl plik

@ -15,7 +15,7 @@
* 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.predict4kotlin
package com.rtbishop.look4sat.domain.predict4kotlin
data class TLE(
val name: String,

Wyświetl plik

@ -9,4 +9,4 @@ class GetSatItems(private val satelliteRepo: SatelliteRepo) {
operator fun invoke(): Flow<List<SatItem>> {
return satelliteRepo.getSatItems()
}
}
}

Wyświetl plik

@ -1,11 +1,11 @@
package com.rtbishop.look4sat.interactors
import com.rtbishop.look4sat.domain.SatelliteRepo
import com.rtbishop.look4sat.predict4kotlin.Satellite
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
class GetSelectedSatellites(private val satelliteRepo: SatelliteRepo) {
suspend operator fun invoke(): List<Satellite> {
return satelliteRepo.getSelectedSatellites()
}
}
}

Wyświetl plik

@ -9,4 +9,4 @@ class GetTransmittersForSat(private val satelliteRepo: SatelliteRepo) {
operator fun invoke(catNum: Int): Flow<List<SatTrans>> {
return satelliteRepo.getTransmittersForSat(catNum)
}
}
}

Wyświetl plik

@ -8,4 +8,4 @@ class ImportDataFromFile(private val satelliteRepo: SatelliteRepo) {
suspend operator fun invoke(stream: InputStream) {
satelliteRepo.importDataFromFile(stream)
}
}
}

Wyświetl plik

@ -7,4 +7,4 @@ class ImportDataFromWeb(private val satelliteRepo: SatelliteRepo) {
suspend operator fun invoke(sources: List<String>) {
satelliteRepo.importDataFromWeb(sources)
}
}
}

Wyświetl plik

@ -7,4 +7,4 @@ class UpdateEntriesSelection(private val satelliteRepo: SatelliteRepo) {
suspend operator fun invoke(catNums: List<Int>, isSelected: Boolean) {
satelliteRepo.updateEntriesSelection(catNums, isSelected)
}
}
}

Wyświetl plik

@ -18,4 +18,4 @@ android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
kotlin.code.style=official

Wyświetl plik

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip