mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Various tweaks and minor refactoring, fixed issue #84
This commit is contained in:
1 parent
ec553bf429
commit
abb1bd4718
22 files changed
+321
-87
No files matched your search
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "f43ab966fb7b0508057c359530582ff5",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "entries",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`comment` TEXT, `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, `orbitalPeriod` REAL NOT NULL, `isDeepSpace` INTEGER NOT NULL, PRIMARY KEY(`catnum`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "comment",
|
||||
"columnName": "comment",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.epoch",
|
||||
"columnName": "epoch",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.meanmo",
|
||||
"columnName": "meanmo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.eccn",
|
||||
"columnName": "eccn",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.incl",
|
||||
"columnName": "incl",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.raan",
|
||||
"columnName": "raan",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.argper",
|
||||
"columnName": "argper",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.meanan",
|
||||
"columnName": "meanan",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.catnum",
|
||||
"columnName": "catnum",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.bstar",
|
||||
"columnName": "bstar",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.xincl",
|
||||
"columnName": "xincl",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.xnodeo",
|
||||
"columnName": "xnodeo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.omegao",
|
||||
"columnName": "omegao",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.xmo",
|
||||
"columnName": "xmo",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.xno",
|
||||
"columnName": "xno",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.orbitalPeriod",
|
||||
"columnName": "orbitalPeriod",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "data.isDeepSpace",
|
||||
"columnName": "isDeepSpace",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"catnum"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "radios",
|
||||
"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, `comment` TEXT, 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
|
||||
},
|
||||
{
|
||||
"fieldPath": "comment",
|
||||
"columnName": "comment",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"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, 'f43ab966fb7b0508057c359530582ff5')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,26 @@ package com.rtbishop.look4sat.framework.data
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.rtbishop.look4sat.framework.data.dao.EntriesDao
|
||||
import com.rtbishop.look4sat.framework.data.dao.RadiosDao
|
||||
import com.rtbishop.look4sat.framework.model.SatEntry
|
||||
import com.rtbishop.look4sat.framework.model.SatRadio
|
||||
|
||||
@Database(entities = [SatEntry::class, SatRadio::class], version = 1, exportSchema = true)
|
||||
@Database(entities = [SatEntry::class, SatRadio::class], version = 2, exportSchema = true)
|
||||
abstract class LocalDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun entriesDao(): EntriesDao
|
||||
|
||||
abstract fun radiosDao(): RadiosDao
|
||||
}
|
||||
|
||||
val MIGRATION_1_2 = object : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE entries_backup (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, orbitalPeriod REAL NOT NULL, isDeepSpace INTEGER NOT NULL, comment TEXT, PRIMARY KEY(catnum))")
|
||||
database.execSQL("INSERT INTO entries_backup (name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar, xincl, xnodeo, omegao, xmo, xno, orbitalPeriod, isDeepSpace, comment) SELECT name, epoch, meanmo, eccn, incl, raan, argper, meanan, catnum, bstar, xincl, xnodeo, omegao, xmo, xno, 1440 / meanmo, 1440 / meanmo >= 225.0, comment FROM entries")
|
||||
database.execSQL("DROP TABLE entries")
|
||||
database.execSQL("ALTER TABLE entries_backup RENAME TO entries")
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class LocalEntrySource(private val entriesDao: EntriesDao) : ILocalEntrySource {
|
||||
val selectedSatellites = mutableListOf<Satellite>()
|
||||
ids.chunked(999).forEach { idsPart ->
|
||||
val entries = entriesDao.getEntriesWithIds(idsPart)
|
||||
selectedSatellites.addAll(entries.map { entry -> entry.data.createSat() })
|
||||
selectedSatellites.addAll(entries.map { entry -> entry.data.getSatellite() })
|
||||
}
|
||||
return selectedSatellites
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ object BaseModule {
|
||||
@Singleton
|
||||
fun provideDataRepository(@ApplicationContext context: Context): IDataRepository {
|
||||
val db = Room.databaseBuilder(context, LocalDatabase::class.java, "Look4SatDb")
|
||||
.fallbackToDestructiveMigration().build()
|
||||
.addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
|
||||
val parser = DataParser(Dispatchers.Default)
|
||||
val fileSource = FileDataSource(context.contentResolver, Dispatchers.IO)
|
||||
val entries = LocalEntrySource(db.entriesDao())
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.view.View
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
@@ -68,3 +70,15 @@ fun <T> Fragment.getNavResult(@IdRes id: Int, key: String, onResult: (result: T)
|
||||
fun <T> Fragment.setNavResult(key: String, value: T) {
|
||||
findNavController().previousBackStackEntry?.savedStateHandle?.set(key, value)
|
||||
}
|
||||
|
||||
fun View.clickWithDebounce(debounceTime: Long = 875L, action: () -> Unit) {
|
||||
this.setOnClickListener(object : View.OnClickListener {
|
||||
private var lastClickTime: Long = 0
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (SystemClock.elapsedRealtime() - lastClickTime < debounceTime) return
|
||||
else action()
|
||||
lastClickTime = SystemClock.elapsedRealtime()
|
||||
}
|
||||
})
|
||||
}
|
||||
+9
-8
@@ -31,6 +31,7 @@ import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.presentation.clickWithDebounce
|
||||
import com.rtbishop.look4sat.presentation.getNavResult
|
||||
import com.rtbishop.look4sat.presentation.setNavResult
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -58,21 +59,21 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
||||
}
|
||||
})
|
||||
}
|
||||
entriesBtnBack.setOnClickListener { findNavController().navigateUp() }
|
||||
entriesBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
||||
entriesSearch.doOnTextChanged { text, _, _, _ -> viewModel.setQuery(text.toString()) }
|
||||
entriesBtnModes.setOnClickListener {
|
||||
entriesBtnModes.clickWithDebounce {
|
||||
val direction = EntriesFragmentDirections.entriesToModes()
|
||||
findNavController().navigate(direction)
|
||||
}
|
||||
entriesBtnSelect.setOnClickListener { viewModel.selectCurrentItems(true) }
|
||||
entriesBtnClear.setOnClickListener { viewModel.selectCurrentItems(false) }
|
||||
entriesFab.setOnClickListener {
|
||||
setNavResult("selection", viewModel.saveSelection())
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
entriesBtnSelect.clickWithDebounce { viewModel.selectCurrentItems(true) }
|
||||
entriesBtnClear.clickWithDebounce { viewModel.selectCurrentItems(false) }
|
||||
}
|
||||
viewModel.satData.observe(viewLifecycleOwner) { satData ->
|
||||
handleSatData(satData, entriesAdapter)
|
||||
binding.entriesFab.clickWithDebounce {
|
||||
setNavResult("selection", viewModel.saveSelection())
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
}
|
||||
getNavResult<List<String>>(R.id.nav_entries, "modes") { modes ->
|
||||
viewModel.saveSelectedModes(modes)
|
||||
|
||||
@@ -18,10 +18,8 @@
|
||||
package com.rtbishop.look4sat.presentation.mapScreen
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
|
||||
data class MapData(
|
||||
val pass: Satellite,
|
||||
val catNum: Int,
|
||||
val name: String,
|
||||
val aosTime: String,
|
||||
@@ -32,6 +30,7 @@ data class MapData(
|
||||
val velocity: Double,
|
||||
val qthLoc: String,
|
||||
val osmPos: GeoPos,
|
||||
val period: Double,
|
||||
val phase: Double,
|
||||
val eclipsed: Boolean
|
||||
)
|
||||
@@ -33,6 +33,7 @@ import com.rtbishop.look4sat.databinding.FragmentMapBinding
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.presentation.clickWithDebounce
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.osmdroid.config.Configuration
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
@@ -98,7 +99,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
||||
// add overlays: 0 - GSP, 1 - SatTrack, 2 - SatFootprint, 3 - SatIcons
|
||||
overlays.addAll(Array(4) { FolderOverlay() })
|
||||
}
|
||||
mapBtnBack.setOnClickListener { findNavController().navigateUp() }
|
||||
mapBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
||||
mapBtnPrev.setOnClickListener { viewModel.scrollSelection(true) }
|
||||
mapBtnNext.setOnClickListener { viewModel.scrollSelection(false) }
|
||||
}
|
||||
@@ -199,24 +200,22 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
||||
private fun handleMapData(mapData: MapData) {
|
||||
binding.apply {
|
||||
mapTimer.text = mapData.aosTime
|
||||
mapDataPeriod.text = String.format(getString(R.string.map_period, mapData.period))
|
||||
mapDataPhase.text = String.format(getString(R.string.map_phase), mapData.phase)
|
||||
mapAzimuth.text = String.format(getString(R.string.map_azimuth), mapData.azimuth)
|
||||
mapElevation.text = String.format(getString(R.string.map_elevation), mapData.elevation)
|
||||
mapDataId.text = String.format(getString(R.string.map_sat_id), mapData.catNum)
|
||||
mapDataQth.text = String.format(getString(R.string.map_qth), mapData.qthLoc)
|
||||
mapDataAlt.text = String.format(getString(R.string.map_altitude), mapData.altitude)
|
||||
mapDataDst.text = String.format(getString(R.string.map_distance), mapData.range)
|
||||
mapDataVel.text = String.format(getString(R.string.map_velocity), mapData.velocity)
|
||||
mapDataLat.text =
|
||||
String.format(getString(R.string.map_latitude), mapData.osmPos.lat)
|
||||
mapDataLon.text =
|
||||
String.format(getString(R.string.map_longitude), mapData.osmPos.lon)
|
||||
mapDataPhase.text = String.format(getString(R.string.map_phase), mapData.phase)
|
||||
mapDataQth.text = String.format(getString(R.string.map_qth), mapData.qthLoc)
|
||||
mapDataVel.text = String.format(getString(R.string.map_velocity), mapData.velocity)
|
||||
if (mapData.eclipsed) {
|
||||
val eclipsed = getString(R.string.map_eclipsed)
|
||||
mapDataVisibility.text = String.format(getString(R.string.map_visibility), eclipsed)
|
||||
mapDataVisibility.text = getString(R.string.map_eclipsed)
|
||||
} else {
|
||||
val visible = getString(R.string.map_visible)
|
||||
mapDataVisibility.text = String.format(getString(R.string.map_visibility), visible)
|
||||
mapDataVisibility.text = getString(R.string.map_visible)
|
||||
}
|
||||
}
|
||||
binding.mapView.invalidate()
|
||||
|
||||
@@ -117,7 +117,7 @@ class MapViewModel @Inject constructor(
|
||||
private suspend fun getSatTrack(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||
val satTracks = mutableListOf<List<GeoPos>>()
|
||||
val currentTrack = mutableListOf<GeoPos>()
|
||||
val endDate = Date(date.time + (satellite.orbitalPeriod * 2.4 * 60000L).toLong())
|
||||
val endDate = Date(date.time + (satellite.data.orbitalPeriod * 2.4 * 60000L).toLong())
|
||||
var oldLongitude = 0.0
|
||||
satelliteManager.getTrack(satellite, pos, date.time, endDate.time).forEach { satPos ->
|
||||
val osmLat = clipLat(satPos.latitude.toDegrees())
|
||||
@@ -180,11 +180,12 @@ class MapViewModel @Inject constructor(
|
||||
val osmLon = clipLon(satPos.longitude.toDegrees())
|
||||
val osmPos = GeoPos(osmLat, osmLon)
|
||||
val qthLoc = QthConverter.positionToQth(osmPos.lat, osmPos.lon) ?: "-- --"
|
||||
val velocity = satPos.getOrbitalVelocity()
|
||||
val phase = satPos.phase.toDegrees()
|
||||
val visibility = satPos.eclipsed
|
||||
val satData = MapData(
|
||||
sat, sat.data.catnum, sat.data.name, aosTime, azimuth, elevation, satPos.distance,
|
||||
satPos.altitude, satPos.getOrbitalVelocity(), qthLoc, osmPos, phase, visibility
|
||||
sat.data.catnum, sat.data.name, aosTime, azimuth, elevation, satPos.distance,
|
||||
satPos.altitude, velocity, qthLoc, osmPos, sat.data.orbitalPeriod, phase, visibility
|
||||
)
|
||||
_mapData.postValue(satData)
|
||||
}
|
||||
|
||||
+12
-7
@@ -31,6 +31,7 @@ import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentPassesBinding
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.presentation.clickWithDebounce
|
||||
import com.rtbishop.look4sat.presentation.getNavResult
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -60,16 +61,16 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
||||
}
|
||||
})
|
||||
}
|
||||
passesBtnRefresh.setOnClickListener { viewModel.calculatePasses() }
|
||||
passesBtnMap.setOnClickListener {
|
||||
passesBtnRefresh.clickWithDebounce { viewModel.calculatePasses() }
|
||||
passesBtnMap.clickWithDebounce {
|
||||
val dir = PassesFragmentDirections.globalToMap()
|
||||
findNavController().navigate(dir)
|
||||
}
|
||||
passesBtnFilter.setOnClickListener {
|
||||
passesBtnFilter.clickWithDebounce {
|
||||
val dir = PassesFragmentDirections.passesToFilter()
|
||||
findNavController().navigate(dir)
|
||||
}
|
||||
passesBtnSettings.setOnClickListener {
|
||||
passesBtnSettings.clickWithDebounce {
|
||||
val dir = PassesFragmentDirections.globalToSettings()
|
||||
findNavController().navigate(dir)
|
||||
}
|
||||
@@ -118,12 +119,12 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
||||
private fun handleEntriesTotal(number: Int) {
|
||||
binding?.run {
|
||||
if (number > 0) {
|
||||
passesFab.setOnClickListener {
|
||||
passesFab.clickWithDebounce {
|
||||
val direction = PassesFragmentDirections.globalToEntries()
|
||||
findNavController().navigate(direction)
|
||||
}
|
||||
} else {
|
||||
passesFab.setOnClickListener {
|
||||
passesFab.clickWithDebounce {
|
||||
val errorMessage = getString(R.string.passes_empty_db)
|
||||
Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -139,13 +140,17 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
||||
tickMainTimer(state.data)
|
||||
if (state.data.isNotEmpty()) { // show new passes list
|
||||
passesEmpty.visibility = View.INVISIBLE
|
||||
passesProgress.visibility = View.INVISIBLE
|
||||
} else { // show no passes message
|
||||
passesEmpty.visibility = View.VISIBLE
|
||||
passesProgress.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
is DataState.Loading -> {
|
||||
// refreshAnimator?.start()
|
||||
passesBtnRefresh.isEnabled = false
|
||||
// passesBtnRefresh.isEnabled = false
|
||||
passesEmpty.visibility = View.INVISIBLE
|
||||
passesProgress.visibility = View.VISIBLE
|
||||
passesTimer.text = 0L.toTimerString()
|
||||
}
|
||||
else -> {}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentRadarBinding
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.presentation.clickWithDebounce
|
||||
import com.rtbishop.look4sat.utility.toDegrees
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -100,13 +101,13 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
||||
viewModel.orientation.observe(viewLifecycleOwner) { value ->
|
||||
radarView?.setOrientation(value.first, value.second, value.third)
|
||||
}
|
||||
radarBtnBack.setOnClickListener { findNavController().navigateUp() }
|
||||
radarBtnMap.setOnClickListener {
|
||||
radarBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
||||
radarBtnMap.clickWithDebounce {
|
||||
val direction = RadarFragmentDirections.globalToMap(pass.catNum)
|
||||
findNavController().navigate(direction)
|
||||
}
|
||||
radarBtnNotify.isEnabled = false
|
||||
radarBtnSettings.setOnClickListener {
|
||||
radarBtnSettings.clickWithDebounce {
|
||||
val direction = RadarFragmentDirections.globalToSettings()
|
||||
findNavController().navigate(direction)
|
||||
}
|
||||
|
||||
+11
-10
@@ -36,6 +36,7 @@ import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentSettingsBinding
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.presentation.clickWithDebounce
|
||||
import com.rtbishop.look4sat.presentation.getNavResult
|
||||
import com.rtbishop.look4sat.utility.isValidIPv4
|
||||
import com.rtbishop.look4sat.utility.isValidPort
|
||||
@@ -64,19 +65,19 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentSettingsBinding.bind(view).apply {
|
||||
settingsBtnBack.setOnClickListener { findNavController().navigateUp() }
|
||||
settingsBtnBack.clickWithDebounce { findNavController().navigateUp() }
|
||||
settingsScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, y, _, newY ->
|
||||
if (y > newY) settingsFab.hide() else settingsFab.show()
|
||||
})
|
||||
settingsAbout.aboutVersion.text =
|
||||
String.format(getString(R.string.app_version), BuildConfig.VERSION_NAME)
|
||||
settingsBtnGithub.setOnClickListener {
|
||||
settingsBtnGithub.clickWithDebounce {
|
||||
gotoUrl("https://github.com/rt-bishop/Look4Sat/")
|
||||
}
|
||||
settingsFab.setOnClickListener {
|
||||
settingsFab.clickWithDebounce {
|
||||
gotoUrl("https://ko-fi.com/rt_bishop")
|
||||
}
|
||||
settingsBtnFdroid.setOnClickListener {
|
||||
settingsBtnFdroid.clickWithDebounce {
|
||||
gotoUrl("https://f-droid.org/en/packages/com.rtbishop.look4sat/")
|
||||
}
|
||||
}
|
||||
@@ -96,14 +97,14 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
private fun setupLocationCard() {
|
||||
binding.run {
|
||||
setPositionText(viewModel.getStationPosition())
|
||||
settingsLocation.locationBtnGps.setOnClickListener {
|
||||
settingsLocation.locationBtnGps.clickWithDebounce {
|
||||
locationRequest.launch(arrayOf(locationFine, locationCoarse))
|
||||
}
|
||||
settingsLocation.locationBtnManual.setOnClickListener {
|
||||
settingsLocation.locationBtnManual.clickWithDebounce {
|
||||
val action = SettingsFragmentDirections.globalToPosition()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
settingsLocation.locationBtnQth.setOnClickListener {
|
||||
settingsLocation.locationBtnQth.clickWithDebounce {
|
||||
val action = SettingsFragmentDirections.globalToLocator()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
@@ -118,12 +119,12 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
|
||||
private fun setupDataCard() {
|
||||
binding.run {
|
||||
settingsData.dataBtnFile.setOnClickListener { contentRequest.launch("*/*") }
|
||||
settingsData.dataBtnWeb.setOnClickListener {
|
||||
settingsData.dataBtnWeb.clickWithDebounce {
|
||||
val action = SettingsFragmentDirections.settingsToSources()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
settingsData.dataBtnClear.setOnClickListener { viewModel.clearData() }
|
||||
settingsData.dataBtnFile.clickWithDebounce { contentRequest.launch("*/*") }
|
||||
settingsData.dataBtnClear.clickWithDebounce { viewModel.clearData() }
|
||||
viewModel.entriesTotal.observe(viewLifecycleOwner) { number ->
|
||||
val entriesFormat = getString(R.string.data_entries)
|
||||
settingsData.dataEntries.text = String.format(entriesFormat, number)
|
||||
|
||||
@@ -89,8 +89,8 @@
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/map_azimuth"
|
||||
app:layout_constraintBottom_toTopOf="@+id/map_data_alt"
|
||||
app:layout_constraintStart_toStartOf="@+id/map_data_visibility"
|
||||
app:layout_constraintTop_toBottomOf="@+id/map_data_visibility" />
|
||||
app:layout_constraintStart_toStartOf="@+id/map_data_period"
|
||||
app:layout_constraintTop_toBottomOf="@+id/map_data_period" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/map_data_lat"
|
||||
@@ -142,13 +142,13 @@
|
||||
app:layout_constraintEnd_toEndOf="@+id/map_data_phase" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/map_data_visibility"
|
||||
android:id="@+id/map_data_period"
|
||||
style="@style/WorldMapText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:text="@string/map_visibility"
|
||||
android:text="@string/map_period"
|
||||
app:layout_constraintBottom_toTopOf="@+id/map_azimuth"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
@@ -160,7 +160,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:text="@string/map_phase"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/map_data_visibility"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/map_data_period"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
@@ -214,7 +214,7 @@
|
||||
app:layout_constraintTop_toTopOf="@+id/map_coordinator" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/map_data_id"
|
||||
android:id="@+id/map_data_visibility"
|
||||
style="@style/WorldMapText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -223,7 +223,7 @@
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_horizontal"
|
||||
android:maxLines="1"
|
||||
android:text="@string/map_sat_id"
|
||||
android:text="@string/map_visible"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/map_btn_prev"
|
||||
app:layout_constraintEnd_toStartOf="@+id/map_btn_next"
|
||||
app:layout_constraintStart_toEndOf="@+id/map_btn_prev" />
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/passes_progress"
|
||||
style="@style/RecyclerProgress" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/passes_empty"
|
||||
android:layout_width="match_parent"
|
||||
@@ -59,10 +63,10 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/passes_empty_img"
|
||||
android:layout_width="180dp"
|
||||
android:layout_height="180dp"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:contentDescription="@string/passes_error"
|
||||
android:src="@drawable/ic_list"
|
||||
app:tint="@color/textDisabled" />
|
||||
@@ -74,7 +78,7 @@
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:text="@string/passes_error"
|
||||
android:textSize="@dimen/text_size_large" />
|
||||
android:textSize="@dimen/text_size_mediumLarge" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<string name="map_visible">Видимый</string>
|
||||
<string name="map_eclipsed">В тени</string>
|
||||
<string name="map_visibility">Видимость: %s</string>
|
||||
<string name="map_period">Период: %.0fмин</string>
|
||||
<string name="map_phase">Фаза: %.1f°</string>
|
||||
<string name="map_altitude">Высота: %.0fкм</string>
|
||||
<string name="map_distance">Дистанция: %.0fкм</string>
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
<string name="map_visible">Visible</string>
|
||||
<string name="map_eclipsed">Eclipsed</string>
|
||||
<string name="map_visibility">Visibility: %s</string>
|
||||
<string name="map_period">Period: %.0f min</string>
|
||||
<string name="map_phase">Phase: %.1f°</string>
|
||||
<string name="map_altitude">Altitude: %.0f km</string>
|
||||
<string name="map_distance">Distance: %.0f km</string>
|
||||
|
||||
@@ -102,8 +102,8 @@
|
||||
</style>
|
||||
|
||||
<style name="RecyclerProgress">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_width">72dp</item>
|
||||
<item name="android:layout_height">72dp</item>
|
||||
<item name="android:layout_gravity">center</item>
|
||||
</style>
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@ class DataRepository(
|
||||
override fun updateFromFile(uri: String) {
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
_updateState.value = DataState.Loading
|
||||
fileSource.getDataStream(uri)?.let { fileStream ->
|
||||
fileSource.getDataStream(uri)?.let { stream ->
|
||||
delay(updateStateDelay)
|
||||
entrySource.insertEntries(importSatellites(fileStream))
|
||||
entrySource.insertEntries(importSatellites(stream))
|
||||
}
|
||||
_updateState.value = DataState.Success(0L)
|
||||
}
|
||||
@@ -67,23 +67,20 @@ class DataRepository(
|
||||
override fun updateFromWeb(urls: List<String>) {
|
||||
_updateState.value = DataState.Loading
|
||||
repositoryScope.launch(exceptionHandler) {
|
||||
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
|
||||
val streamsMap = mutableMapOf<String, InputStream?>()
|
||||
val streams = mutableListOf<InputStream>()
|
||||
val entries = mutableListOf<SatEntry>()
|
||||
urls.forEach { jobsMap[it] = async { remoteSource.getDataStream(it) } }
|
||||
jobsMap.forEach { job -> streamsMap[job.key] = job.value.await() }
|
||||
streamsMap.forEach { stream ->
|
||||
stream.value?.let { inputStream ->
|
||||
val jobs = urls.associateWith { url -> async { remoteSource.getDataStream(url) } }
|
||||
jobs.mapValues { job -> job.value.await() }.forEach { result ->
|
||||
result.value?.let { stream ->
|
||||
when {
|
||||
stream.key.contains("=csv", true) -> {
|
||||
val tles = dataParser.parseCSVStream(inputStream)
|
||||
entries.addAll(tles.map { tle -> SatEntry(tle) })
|
||||
result.key.contains("=csv", true) -> {
|
||||
val orbitalData = dataParser.parseCSVStream(stream)
|
||||
entries.addAll(orbitalData.map { data -> SatEntry(data) })
|
||||
}
|
||||
stream.key.contains(".zip", true) -> {
|
||||
streams.add(ZipInputStream(inputStream).apply { nextEntry })
|
||||
result.key.contains(".zip", true) -> {
|
||||
streams.add(ZipInputStream(stream).apply { nextEntry })
|
||||
}
|
||||
else -> streams.add(inputStream)
|
||||
else -> streams.add(stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,6 +110,6 @@ class DataRepository(
|
||||
}
|
||||
|
||||
private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
|
||||
return dataParser.parseTLEStream(stream).map { tle -> SatEntry(tle) }
|
||||
return dataParser.parseTLEStream(stream).map { data -> SatEntry(data) }
|
||||
}
|
||||
}
|
||||
@@ -33,13 +33,9 @@ data class OrbitalData(
|
||||
val omegao: Double = argper * DEG2RAD,
|
||||
val xmo: Double = meanan * DEG2RAD,
|
||||
val xno: Double = meanmo * TWO_PI / MIN_PER_DAY,
|
||||
val isDeepspace: Boolean = meanmo < 6.4
|
||||
val orbitalPeriod: Double = MIN_PER_DAY / meanmo,
|
||||
// Space objects are classified as NearEarth (period < 225 min) or DeepSpace (period >= 225 min)
|
||||
val isDeepSpace: Boolean = orbitalPeriod >= 225.0
|
||||
) {
|
||||
|
||||
fun createSat(): Satellite {
|
||||
return when {
|
||||
this.isDeepspace -> DeepSpaceSat(this)
|
||||
else -> NearEarthSat(this)
|
||||
}
|
||||
}
|
||||
fun getSatellite(): Satellite = if (isDeepSpace) DeepSpaceSat(this) else NearEarthSat(this)
|
||||
}
|
||||
@@ -31,5 +31,5 @@ data class SatPass(
|
||||
) {
|
||||
val catNum: Int = satellite.data.catnum
|
||||
val name: String = satellite.data.name
|
||||
val isDeepSpace: Boolean = satellite.data.isDeepspace
|
||||
val isDeepSpace: Boolean = satellite.data.isDeepSpace
|
||||
}
|
||||
@@ -47,7 +47,6 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
private var gsPosTheta = 0.0
|
||||
private var julUTC = 0.0
|
||||
private var perigee = 0.0
|
||||
val orbitalPeriod = 24 * 60 / data.meanmo
|
||||
var qoms24 = 0.0
|
||||
var s4 = 0.0
|
||||
|
||||
@@ -108,7 +107,7 @@ abstract class Satellite(val data: OrbitalData) {
|
||||
}
|
||||
|
||||
private fun calculateSDP4orSGP4(tsince: Double) {
|
||||
if (data.isDeepspace) (this as DeepSpaceSat).calculateSDP4(tsince)
|
||||
if (data.isDeepSpace) (this as DeepSpaceSat).calculateSDP4(tsince)
|
||||
else (this as NearEarthSat).calculateSGP4(tsince)
|
||||
}
|
||||
|
||||
|
||||
@@ -118,13 +118,13 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
|
||||
private fun Satellite.getPasses(pos: GeoPos, time: Long, hours: Int): List<SatPass> {
|
||||
val passes = mutableListOf<SatPass>()
|
||||
val endDate = time + hours * 60L * 60L * 1000L
|
||||
val quarterOrbitMin = (this.orbitalPeriod / 4.0).toInt()
|
||||
val quarterOrbitMin = (this.data.orbitalPeriod / 4.0).toInt()
|
||||
var startDate = time
|
||||
var shouldRewind = true
|
||||
var lastAosDate: Long
|
||||
var count = 0
|
||||
if (this.willBeSeen(pos)) {
|
||||
if (this.data.isDeepspace) {
|
||||
if (this.data.isDeepSpace) {
|
||||
passes.add(getGeoPass(this, pos, time))
|
||||
} else {
|
||||
do {
|
||||
@@ -160,7 +160,7 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa
|
||||
}
|
||||
|
||||
private fun getLeoPass(sat: Satellite, pos: GeoPos, time: Long, rewind: Boolean): SatPass {
|
||||
val quarterOrbitMin = (sat.orbitalPeriod / 4.0).toInt()
|
||||
val quarterOrbitMin = (sat.data.orbitalPeriod / 4.0).toInt()
|
||||
var calendarTimeMillis = time
|
||||
var elevation: Double
|
||||
var maxElevation = 0.0
|
||||
|
||||
Reference in new issue
Block a user