From abb1bd4718211d9e92249de6cc01a0586ce95d25 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Fri, 11 Mar 2022 12:44:11 +0000 Subject: [PATCH] Various tweaks and minor refactoring, fixed issue #84 --- .../2.json | 204 ++++++++++++++++++ .../look4sat/framework/data/LocalDatabase.kt | 13 +- .../framework/data/LocalEntrySource.kt | 2 +- .../rtbishop/look4sat/injection/BaseModule.kt | 2 +- .../look4sat/presentation/MainActivity.kt | 14 ++ .../entriesScreen/EntriesFragment.kt | 17 +- .../presentation/mapScreen/MapData.kt | 3 +- .../presentation/mapScreen/MapFragment.kt | 17 +- .../presentation/mapScreen/MapViewModel.kt | 7 +- .../passesScreen/PassesFragment.kt | 19 +- .../presentation/radarScreen/RadarFragment.kt | 7 +- .../settingsScreen/SettingsFragment.kt | 21 +- app/src/main/res/layout/fragment_map.xml | 14 +- app/src/main/res/layout/fragment_passes.xml | 12 +- app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 4 +- .../look4sat/domain/data/DataRepository.kt | 27 ++- .../look4sat/domain/predict/OrbitalData.kt | 12 +- .../look4sat/domain/predict/SatPass.kt | 2 +- .../look4sat/domain/predict/Satellite.kt | 3 +- .../domain/predict/SatelliteManager.kt | 6 +- 22 files changed, 321 insertions(+), 87 deletions(-) create mode 100644 app/schemas/com.rtbishop.look4sat.framework.data.LocalDatabase/2.json diff --git a/app/schemas/com.rtbishop.look4sat.framework.data.LocalDatabase/2.json b/app/schemas/com.rtbishop.look4sat.framework.data.LocalDatabase/2.json new file mode 100644 index 00000000..ddac2a28 --- /dev/null +++ b/app/schemas/com.rtbishop.look4sat.framework.data.LocalDatabase/2.json @@ -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')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalDatabase.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalDatabase.kt index 9bf761e3..7e3635f3 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalDatabase.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalDatabase.kt @@ -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") + } +} diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalEntrySource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalEntrySource.kt index 61c870b9..c09d9be3 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalEntrySource.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/LocalEntrySource.kt @@ -37,7 +37,7 @@ class LocalEntrySource(private val entriesDao: EntriesDao) : ILocalEntrySource { val selectedSatellites = mutableListOf() 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 } diff --git a/app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt b/app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt index d1caffc8..266b9cab 100644 --- a/app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt +++ b/app/src/main/java/com/rtbishop/look4sat/injection/BaseModule.kt @@ -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()) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt index 8433c325..7bdedb43 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt @@ -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 Fragment.getNavResult(@IdRes id: Int, key: String, onResult: (result: T) fun 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() + } + }) +} diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesFragment.kt index b186ff21..b7b40d69 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesFragment.kt @@ -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>(R.id.nav_entries, "modes") { modes -> viewModel.saveSelectedModes(modes) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapData.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapData.kt index ba5d7e6c..4b2c06c2 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapData.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapData.kt @@ -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 ) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt index 1b58e13c..c874e214 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt @@ -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() diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt index ed1ac71a..1d3a9f1a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt @@ -117,7 +117,7 @@ class MapViewModel @Inject constructor( private suspend fun getSatTrack(satellite: Satellite, pos: GeoPos, date: Date) { val satTracks = mutableListOf>() val currentTrack = mutableListOf() - 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) } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt index 4dc3d25f..b69cf989 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt @@ -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 -> {} diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt index 88c0cba0..45e91a34 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radarScreen/RadarFragment.kt @@ -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) } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt index 91666705..85be5278 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settingsScreen/SettingsFragment.kt @@ -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) diff --git a/app/src/main/res/layout/fragment_map.xml b/app/src/main/res/layout/fragment_map.xml index c7426cf0..18b3bf6d 100644 --- a/app/src/main/res/layout/fragment_map.xml +++ b/app/src/main/res/layout/fragment_map.xml @@ -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" /> @@ -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" /> diff --git a/app/src/main/res/layout/fragment_passes.xml b/app/src/main/res/layout/fragment_passes.xml index a569e1e1..7acecd3e 100644 --- a/app/src/main/res/layout/fragment_passes.xml +++ b/app/src/main/res/layout/fragment_passes.xml @@ -46,6 +46,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + + @@ -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" /> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ad454ab1..0ba55032 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -109,6 +109,7 @@ Видимый В тени Видимость: %s + Период: %.0fмин Фаза: %.1f° Высота: %.0fкм Дистанция: %.0fкм diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f315d571..7c419165 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,6 +110,7 @@ Visible Eclipsed Visibility: %s + Period: %.0f min Phase: %.1f° Altitude: %.0f km Distance: %.0f km diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 6d29b95b..fde65210 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -102,8 +102,8 @@ diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt b/base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt index 42eba294..a9a30deb 100644 --- a/base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt +++ b/base/src/main/java/com/rtbishop/look4sat/domain/data/DataRepository.kt @@ -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) { _updateState.value = DataState.Loading repositoryScope.launch(exceptionHandler) { - val jobsMap = mutableMapOf>() - val streamsMap = mutableMapOf() val streams = mutableListOf() val entries = mutableListOf() - 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 { - return dataParser.parseTLEStream(stream).map { tle -> SatEntry(tle) } + return dataParser.parseTLEStream(stream).map { data -> SatEntry(data) } } } diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt index 37065018..9a05da11 100644 --- a/base/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt +++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/OrbitalData.kt @@ -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) } diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt index 5bd4b136..bf513399 100644 --- a/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt +++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatPass.kt @@ -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 } diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt index b99a6110..4743441e 100644 --- a/base/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt +++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/Satellite.kt @@ -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) } diff --git a/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt index fd85d583..6c1fbfcb 100644 --- a/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt +++ b/base/src/main/java/com/rtbishop/look4sat/domain/predict/SatelliteManager.kt @@ -118,13 +118,13 @@ class SatelliteManager(private val defaultDispatcher: CoroutineDispatcher) : ISa private fun Satellite.getPasses(pos: GeoPos, time: Long, hours: Int): List { val passes = mutableListOf() 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