mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-05 12:07:25 +00:00
Interfaces and classes refactoring and code cleanup
This commit is contained in:
1 parent
0c71c4db29
commit
74cbad5127
43 files changed
+403
-521
No files matched your search
File renamed without changes.
@@ -7,7 +7,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".presentation.Look4SatApplication"
|
android:name=".presentation.MainApplication"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
|||||||
@@ -1,47 +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
|
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatEntry as DomainEntry
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatItem as DomainItem
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio as DomainRadio
|
|
||||||
import com.rtbishop.look4sat.framework.model.SatEntry as FrameworkEntry
|
|
||||||
import com.rtbishop.look4sat.framework.model.SatItem as FrameworkItem
|
|
||||||
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
|
|
||||||
|
|
||||||
fun DomainEntry.toFramework() = FrameworkEntry(this.data, this.comment)
|
|
||||||
|
|
||||||
fun DomainRadio.toFramework() = FrameworkRadio(
|
|
||||||
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
|
|
||||||
this.mode, this.isInverted, this.catnum, this.comment
|
|
||||||
)
|
|
||||||
|
|
||||||
fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.modes, false)
|
|
||||||
|
|
||||||
fun FrameworkRadio.toDomain() = DomainRadio(
|
|
||||||
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
|
|
||||||
this.mode, this.isInverted, this.catnum, this.comment
|
|
||||||
)
|
|
||||||
|
|
||||||
fun List<DomainEntry>.toFrameworkEntries() = this.map { it.toFramework() }
|
|
||||||
|
|
||||||
fun List<DomainRadio>.toFrameworkRadios() = this.map { it.toFramework() }
|
|
||||||
|
|
||||||
fun List<FrameworkItem>.toDomainItems() = this.map { it.toDomain() }
|
|
||||||
|
|
||||||
fun List<FrameworkRadio>.toDomainRadios() = this.map { it.toDomain() }
|
|
||||||
@@ -26,8 +26,8 @@ import android.location.LocationManager
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.domain.ILocationHandler
|
import com.rtbishop.look4sat.domain.ILocationHandler
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.QthConverter
|
import com.rtbishop.look4sat.domain.QthConverter
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
import com.rtbishop.look4sat.domain.model.DataState
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
@@ -41,7 +41,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class LocationHandler @Inject constructor(
|
class LocationHandler @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val settingsHandler: ISettingsHandler,
|
private val settings: ISettings,
|
||||||
) : LocationListener, ILocationHandler {
|
) : LocationListener, ILocationHandler {
|
||||||
|
|
||||||
private val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
private val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
@@ -51,8 +51,8 @@ class LocationHandler @Inject constructor(
|
|||||||
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
private val _stationPosition = MutableStateFlow<DataState<GeoPos>>(DataState.Handled)
|
private val _stationPosition = MutableStateFlow<DataState<GeoPos>>(DataState.Handled)
|
||||||
private var currentLocator = settingsHandler.loadStationLocator()
|
private var currentLocator = settings.loadStationLocator()
|
||||||
private var currentPosition = settingsHandler.loadStationPosition()
|
private var currentPosition = settings.loadStationPosition()
|
||||||
|
|
||||||
override val stationPosition: StateFlow<DataState<GeoPos>> = _stationPosition
|
override val stationPosition: StateFlow<DataState<GeoPos>> = _stationPosition
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ class LocationHandler @Inject constructor(
|
|||||||
QthConverter.positionToQth(newLat, newLon)?.let { locator ->
|
QthConverter.positionToQth(newLat, newLon)?.let { locator ->
|
||||||
currentLocator = locator
|
currentLocator = locator
|
||||||
currentPosition = GeoPos(newLat, newLon)
|
currentPosition = GeoPos(newLat, newLon)
|
||||||
settingsHandler.saveStationLocator(locator)
|
settings.saveStationLocator(locator)
|
||||||
settingsHandler.saveStationPosition(currentPosition)
|
settings.saveStationPosition(currentPosition)
|
||||||
_stationPosition.value = DataState.Success(currentPosition)
|
_stationPosition.value = DataState.Success(currentPosition)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -113,8 +113,8 @@ class LocationHandler @Inject constructor(
|
|||||||
if (position != null) {
|
if (position != null) {
|
||||||
currentLocator = locator
|
currentLocator = locator
|
||||||
currentPosition = position
|
currentPosition = position
|
||||||
settingsHandler.saveStationLocator(locator)
|
settings.saveStationLocator(locator)
|
||||||
settingsHandler.saveStationPosition(currentPosition)
|
settings.saveStationPosition(currentPosition)
|
||||||
_stationPosition.value = DataState.Success(currentPosition)
|
_stationPosition.value = DataState.Success(currentPosition)
|
||||||
} else {
|
} else {
|
||||||
_stationPosition.value = DataState.Error(context.getString(R.string.pref_pos_qth_error))
|
_stationPosition.value = DataState.Error(context.getString(R.string.pref_pos_qth_error))
|
||||||
|
|||||||
@@ -19,15 +19,13 @@ package com.rtbishop.look4sat.framework
|
|||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.presentation.getDouble
|
|
||||||
import com.rtbishop.look4sat.presentation.putDouble
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class SettingsHandler @Inject constructor(private val prefs: SharedPreferences) : ISettingsHandler {
|
class SettingsHandler @Inject constructor(private val prefs: SharedPreferences) : ISettings {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val keyDataSources = "dataSources"
|
const val keyDataSources = "dataSources"
|
||||||
@@ -158,4 +156,12 @@ class SettingsHandler @Inject constructor(private val prefs: SharedPreferences)
|
|||||||
override fun saveDataSources(sources: List<String>) {
|
override fun saveDataSources(sources: List<String>) {
|
||||||
if (sources.isNotEmpty()) prefs.edit { putStringSet(keyDataSources, sources.toSet()) }
|
if (sources.isNotEmpty()) prefs.edit { putStringSet(keyDataSources, sources.toSet()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun SharedPreferences.getDouble(key: String, default: Double): Double {
|
||||||
|
return Double.fromBits(getLong(key, default.toRawBits()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SharedPreferences.Editor.putDouble(key: String, double: Double) {
|
||||||
|
putLong(key, double.toRawBits())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+5
-5
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.framework.local
|
package com.rtbishop.look4sat.framework.data
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.rtbishop.look4sat.framework.model.SatEntry
|
import com.rtbishop.look4sat.framework.model.SatEntry
|
||||||
@@ -23,18 +23,18 @@ import com.rtbishop.look4sat.framework.model.SatItem
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface SatEntriesDao {
|
interface EntriesDao {
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM entries")
|
@Query("SELECT COUNT(*) FROM entries")
|
||||||
fun getEntriesNumber(): Flow<Int>
|
fun getEntriesTotal(): Flow<Int>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
|
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
|
||||||
suspend fun getEntriesWithModes(): List<SatItem>
|
suspend fun getEntriesWithModes(): List<SatItem>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM entries WHERE catnum IN (:catnums)")
|
@Query("SELECT * FROM entries WHERE catnum IN (:selectedIds)")
|
||||||
suspend fun getSelectedEntries(catnums: List<Int>): List<SatEntry>
|
suspend fun getEntriesWithIds(selectedIds: List<Int>): List<SatEntry>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insertEntries(entries: List<SatEntry>)
|
suspend fun insertEntries(entries: List<SatEntry>)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.rtbishop.look4sat.framework.data
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.net.Uri
|
||||||
|
import com.rtbishop.look4sat.data.IProvider
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
class Provider(
|
||||||
|
private val contentResolver: ContentResolver,
|
||||||
|
private val ioDispatcher: CoroutineDispatcher
|
||||||
|
) : IProvider {
|
||||||
|
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
override suspend fun getLocalFileStream(uri: String): InputStream? {
|
||||||
|
return withContext(ioDispatcher) { contentResolver.openInputStream(Uri.parse(uri)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
override suspend fun getRemoteFileStream(url: String): InputStream? {
|
||||||
|
return withContext(ioDispatcher) { URL(url).openStream() }
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-9
@@ -15,23 +15,21 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.framework.local
|
package com.rtbishop.look4sat.framework.data
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.*
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.rtbishop.look4sat.framework.model.SatRadio
|
import com.rtbishop.look4sat.framework.model.SatRadio
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface SatRadiosDao {
|
interface RadiosDao {
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM radios")
|
@Query("SELECT COUNT(*) FROM radios")
|
||||||
fun getRadiosNumber(): Flow<Int>
|
fun getRadiosTotal(): Flow<Int>
|
||||||
|
|
||||||
@Query("SELECT * FROM radios WHERE catnum = :catnum AND isAlive = 1")
|
@Transaction
|
||||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
@Query("SELECT * FROM radios WHERE catnum = :id AND isAlive = 1")
|
||||||
|
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insertRadios(radios: List<SatRadio>)
|
suspend fun insertRadios(radios: List<SatRadio>)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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.data
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.data.IStorage
|
||||||
|
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||||
|
import com.rtbishop.look4sat.domain.model.SatEntry as DomainEntry
|
||||||
|
import com.rtbishop.look4sat.domain.model.SatItem as DomainItem
|
||||||
|
import com.rtbishop.look4sat.domain.model.SatRadio as DomainRadio
|
||||||
|
import com.rtbishop.look4sat.framework.model.SatEntry as FrameworkEntry
|
||||||
|
import com.rtbishop.look4sat.framework.model.SatItem as FrameworkItem
|
||||||
|
import com.rtbishop.look4sat.framework.model.SatRadio as FrameworkRadio
|
||||||
|
|
||||||
|
class Storage(private val entriesDao: EntriesDao, private val radiosDao: RadiosDao) : IStorage {
|
||||||
|
|
||||||
|
override fun getEntriesTotal() = entriesDao.getEntriesTotal()
|
||||||
|
|
||||||
|
override fun getRadiosTotal() = radiosDao.getRadiosTotal()
|
||||||
|
|
||||||
|
override suspend fun getEntriesWithModes(): List<DomainItem> {
|
||||||
|
return entriesDao.getEntriesWithModes().toDomainItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite> {
|
||||||
|
val selectedSatellites = mutableListOf<Satellite>()
|
||||||
|
ids.chunked(999).forEach { idsPart ->
|
||||||
|
val entries = entriesDao.getEntriesWithIds(idsPart)
|
||||||
|
selectedSatellites.addAll(entries.map { entry -> entry.data.createSat() })
|
||||||
|
}
|
||||||
|
return selectedSatellites
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getRadiosWithId(id: Int): List<DomainRadio> {
|
||||||
|
return radiosDao.getRadiosWithId(id).toDomainRadios()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insertEntries(entries: List<DomainEntry>) {
|
||||||
|
entriesDao.insertEntries(entries.toFrameworkEntries())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun insertRadios(radios: List<DomainRadio>) {
|
||||||
|
radiosDao.insertRadios(radios.toFrameworkRadios())
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clearAllData() {
|
||||||
|
entriesDao.deleteEntries()
|
||||||
|
radiosDao.deleteRadios()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DomainEntry.toFramework() = FrameworkEntry(this.data, this.comment)
|
||||||
|
|
||||||
|
private fun DomainRadio.toFramework() = FrameworkRadio(
|
||||||
|
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
|
||||||
|
this.mode, this.isInverted, this.catnum, this.comment
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.modes, false)
|
||||||
|
|
||||||
|
private fun FrameworkRadio.toDomain() = DomainRadio(
|
||||||
|
this.uuid, this.info, this.isAlive, this.downlink, this.uplink,
|
||||||
|
this.mode, this.isInverted, this.catnum, this.comment
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun List<DomainEntry>.toFrameworkEntries() = this.map { entry -> entry.toFramework() }
|
||||||
|
|
||||||
|
private fun List<DomainRadio>.toFrameworkRadios() = this.map { radio -> radio.toFramework() }
|
||||||
|
|
||||||
|
private fun List<FrameworkItem>.toDomainItems() = this.map { item -> item.toDomain() }
|
||||||
|
|
||||||
|
private fun List<FrameworkRadio>.toDomainRadios() = this.map { radio -> radio.toDomain() }
|
||||||
|
}
|
||||||
+4
-4
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.framework.local
|
package com.rtbishop.look4sat.framework.data
|
||||||
|
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
@@ -23,9 +23,9 @@ import com.rtbishop.look4sat.framework.model.SatEntry
|
|||||||
import com.rtbishop.look4sat.framework.model.SatRadio
|
import com.rtbishop.look4sat.framework.model.SatRadio
|
||||||
|
|
||||||
@Database(entities = [SatEntry::class, SatRadio::class], version = 1, exportSchema = true)
|
@Database(entities = [SatEntry::class, SatRadio::class], version = 1, exportSchema = true)
|
||||||
abstract class Look4SatDb : RoomDatabase() {
|
abstract class StorageDb : RoomDatabase() {
|
||||||
|
|
||||||
abstract fun entriesDao(): SatEntriesDao
|
abstract fun entriesDao(): EntriesDao
|
||||||
|
|
||||||
abstract fun radiosDao(): SatRadiosDao
|
abstract fun radiosDao(): RadiosDao
|
||||||
}
|
}
|
||||||
@@ -1,101 +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.local
|
|
||||||
|
|
||||||
import android.content.ContentResolver
|
|
||||||
import android.net.Uri
|
|
||||||
import com.rtbishop.look4sat.data.ILocalSource
|
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatItem
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
|
||||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
|
||||||
import com.rtbishop.look4sat.framework.toDomainItems
|
|
||||||
import com.rtbishop.look4sat.framework.toDomainRadios
|
|
||||||
import com.rtbishop.look4sat.framework.toFrameworkEntries
|
|
||||||
import com.rtbishop.look4sat.framework.toFrameworkRadios
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
class LocalSource(
|
|
||||||
private val entriesDao: SatEntriesDao,
|
|
||||||
private val radiosDao: SatRadiosDao,
|
|
||||||
private val settings: ISettingsHandler,
|
|
||||||
private val resolver: ContentResolver,
|
|
||||||
private val ioDispatcher: CoroutineDispatcher
|
|
||||||
) : ILocalSource {
|
|
||||||
|
|
||||||
override fun getEntriesNumber() = entriesDao.getEntriesNumber()
|
|
||||||
|
|
||||||
override fun getRadiosNumber() = radiosDao.getRadiosNumber()
|
|
||||||
|
|
||||||
override suspend fun getEntriesWithModes(): List<SatItem> {
|
|
||||||
val selectedCatnums = getEntriesSelection()
|
|
||||||
val entriesWithModes = entriesDao.getEntriesWithModes().toDomainItems()
|
|
||||||
entriesWithModes.forEach { entry -> entry.isSelected = entry.catnum in selectedCatnums }
|
|
||||||
return entriesWithModes
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getSelectedEntries(): List<Satellite> {
|
|
||||||
val selectedSatellites = mutableListOf<Satellite>()
|
|
||||||
getEntriesSelection().chunked(999).forEach { catnums ->
|
|
||||||
val entries = entriesDao.getSelectedEntries(catnums)
|
|
||||||
selectedSatellites.addAll(entries.map { entry -> entry.data.createSat() })
|
|
||||||
}
|
|
||||||
return selectedSatellites
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getRadios(catnum: Int): List<SatRadio> {
|
|
||||||
return radiosDao.getRadios(catnum).toDomainRadios()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
override suspend fun getFileStream(uri: String): InputStream? {
|
|
||||||
return withContext(ioDispatcher) { resolver.openInputStream(Uri.parse(uri)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun insertEntries(entries: List<SatEntry>) {
|
|
||||||
entriesDao.insertEntries(entries.toFrameworkEntries())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun insertRadios(radios: List<SatRadio>) {
|
|
||||||
radiosDao.insertRadios(radios.toFrameworkRadios())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun clearAllData() {
|
|
||||||
entriesDao.deleteEntries()
|
|
||||||
radiosDao.deleteRadios()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getDataSources(): List<String> {
|
|
||||||
return withContext(ioDispatcher) { settings.loadDataSources() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setDataSources(sources: List<String>) {
|
|
||||||
withContext(ioDispatcher) { settings.saveDataSources(sources) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun getEntriesSelection(): List<Int> {
|
|
||||||
return withContext(ioDispatcher) { settings.loadEntriesSelection() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setEntriesSelection(catnums: List<Int>) {
|
|
||||||
withContext(ioDispatcher) { settings.saveEntriesSelection(catnums) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.rtbishop.look4sat.framework.remote
|
|
||||||
|
|
||||||
import com.rtbishop.look4sat.data.IRemoteSource
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
class RemoteSource(private val ioDispatcher: CoroutineDispatcher) : IRemoteSource {
|
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
override suspend fun getFileStream(url: String): InputStream? {
|
|
||||||
return withContext(ioDispatcher) { URL(url).openStream() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,18 +20,18 @@ package com.rtbishop.look4sat.injection
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.rtbishop.look4sat.data.DataRepository
|
import com.rtbishop.look4sat.data.DataParser
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
import com.rtbishop.look4sat.data.Repository
|
||||||
import com.rtbishop.look4sat.domain.DataParser
|
|
||||||
import com.rtbishop.look4sat.domain.DataReporter
|
import com.rtbishop.look4sat.domain.DataReporter
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
|
||||||
import com.rtbishop.look4sat.domain.ILocationHandler
|
import com.rtbishop.look4sat.domain.ILocationHandler
|
||||||
|
import com.rtbishop.look4sat.domain.IRepository
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||||
import com.rtbishop.look4sat.framework.LocationHandler
|
import com.rtbishop.look4sat.framework.LocationHandler
|
||||||
import com.rtbishop.look4sat.framework.SettingsHandler
|
import com.rtbishop.look4sat.framework.SettingsHandler
|
||||||
import com.rtbishop.look4sat.framework.local.LocalSource
|
import com.rtbishop.look4sat.framework.data.Provider
|
||||||
import com.rtbishop.look4sat.framework.local.Look4SatDb
|
import com.rtbishop.look4sat.framework.data.Storage
|
||||||
import com.rtbishop.look4sat.framework.remote.RemoteSource
|
import com.rtbishop.look4sat.framework.data.StorageDb
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
@@ -49,24 +49,22 @@ object CoreModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSatelliteRepo(
|
fun provideSatelliteRepo(
|
||||||
settings: ISettingsHandler,
|
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||||
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
|
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
|
||||||
): IDataRepository {
|
): IRepository {
|
||||||
val db = Room.databaseBuilder(context, Look4SatDb::class.java, "Look4SatDb")
|
val db = Room.databaseBuilder(context, StorageDb::class.java, "Look4SatDb")
|
||||||
.fallbackToDestructiveMigration().build()
|
.fallbackToDestructiveMigration().build()
|
||||||
val dataParser = DataParser(defaultDispatcher)
|
val dataParser = DataParser(defaultDispatcher)
|
||||||
val resolver = context.contentResolver
|
val localSource = Storage(db.entriesDao(), db.radiosDao())
|
||||||
val local = LocalSource(db.entriesDao(), db.radiosDao(), settings, resolver, ioDispatcher)
|
val remoteSource = Provider(context.contentResolver, ioDispatcher)
|
||||||
val remote = RemoteSource(ioDispatcher)
|
|
||||||
val repositoryScope = CoroutineScope(SupervisorJob())
|
val repositoryScope = CoroutineScope(SupervisorJob())
|
||||||
return DataRepository(dataParser, local, remote, repositoryScope)
|
return Repository(dataParser, localSource, remoteSource, repositoryScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSettingsHandler(sharedPreferences: SharedPreferences): ISettingsHandler {
|
fun provideSettingsHandler(sharedPreferences: SharedPreferences): ISettings {
|
||||||
return SettingsHandler(sharedPreferences)
|
return SettingsHandler(sharedPreferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +72,9 @@ object CoreModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
fun provideLocationHandler(
|
fun provideLocationHandler(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
settingsHandler: ISettingsHandler
|
settings: ISettings
|
||||||
): ILocationHandler {
|
): ILocationHandler {
|
||||||
return LocationHandler(context, settingsHandler)
|
return LocationHandler(context, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -1,57 +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.presentation
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.annotation.IdRes
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
|
|
||||||
fun Context.toast(text: String, duration: Int = Toast.LENGTH_SHORT) {
|
|
||||||
Toast.makeText(this, text, duration).apply { show() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SharedPreferences.getDouble(key: String, default: Double): Double {
|
|
||||||
return Double.fromBits(getLong(key, default.toRawBits()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SharedPreferences.Editor.putDouble(key: String, double: Double) {
|
|
||||||
putLong(key, double.toRawBits())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Fragment.getNavResult(@IdRes id: Int, key: String, onResult: (result: T) -> Unit) {
|
|
||||||
val backStackEntry = findNavController().getBackStackEntry(id)
|
|
||||||
val observer = LifecycleEventObserver { _, event ->
|
|
||||||
if (event == Lifecycle.Event.ON_RESUME && backStackEntry.savedStateHandle.contains(key)) {
|
|
||||||
backStackEntry.savedStateHandle.get<T>(key)?.let(onResult)
|
|
||||||
backStackEntry.savedStateHandle.remove<T>(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
backStackEntry.lifecycle.addObserver(observer)
|
|
||||||
viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
|
|
||||||
if (event == Lifecycle.Event.ON_DESTROY) backStackEntry.lifecycle.removeObserver(observer)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Fragment.setNavResult(key: String, value: T) {
|
|
||||||
findNavController().previousBackStackEntry?.savedStateHandle?.set(key, value)
|
|
||||||
}
|
|
||||||
@@ -20,23 +20,42 @@ package com.rtbishop.look4sat.presentation
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.viewModels
|
import androidx.annotation.IdRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.rtbishop.look4sat.databinding.ActivityMainBinding
|
import com.rtbishop.look4sat.databinding.ActivityMainBinding
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val mainViewModel: MainViewModel by viewModels()
|
|
||||||
|
|
||||||
@SuppressLint("SourceLockedOrientationActivity")
|
@SuppressLint("SourceLockedOrientationActivity")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(ActivityMainBinding.inflate(layoutInflater).root)
|
setContentView(ActivityMainBinding.inflate(layoutInflater).root)
|
||||||
mainViewModel.calculatePasses()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> Fragment.getNavResult(@IdRes id: Int, key: String, onResult: (result: T) -> Unit) {
|
||||||
|
val backStackEntry = findNavController().getBackStackEntry(id)
|
||||||
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
|
if (event == Lifecycle.Event.ON_RESUME && backStackEntry.savedStateHandle.contains(key)) {
|
||||||
|
backStackEntry.savedStateHandle.get<T>(key)?.let(onResult)
|
||||||
|
backStackEntry.savedStateHandle.remove<T>(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backStackEntry.lifecycle.addObserver(observer)
|
||||||
|
viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
|
||||||
|
if (event == Lifecycle.Event.ON_DESTROY) backStackEntry.lifecycle.removeObserver(observer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Fragment.setNavResult(key: String, value: T) {
|
||||||
|
findNavController().previousBackStackEntry?.savedStateHandle?.set(key, value)
|
||||||
|
}
|
||||||
+1
-1
@@ -21,4 +21,4 @@ import android.app.Application
|
|||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class Look4SatApplication : Application()
|
class MainApplication : Application()
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package com.rtbishop.look4sat.presentation
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
class MainViewModel @Inject constructor(
|
|
||||||
private val predictor: Predictor,
|
|
||||||
private val preferences: ISettingsHandler,
|
|
||||||
private val dataRepository: IDataRepository
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
fun calculatePasses(
|
|
||||||
hoursAhead: Int = preferences.getHoursAhead(),
|
|
||||||
minElevation: Double = preferences.getMinElevation(),
|
|
||||||
timeRef: Long = System.currentTimeMillis()
|
|
||||||
) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
val satellites = dataRepository.getSelectedEntries()
|
|
||||||
val stationPos = preferences.loadStationPosition()
|
|
||||||
predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -39,8 +39,8 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
||||||
|
|
||||||
private lateinit var binding: FragmentEntriesBinding
|
|
||||||
private val viewModel: EntriesViewModel by viewModels()
|
private val viewModel: EntriesViewModel by viewModels()
|
||||||
|
private lateinit var binding: FragmentEntriesBinding
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|||||||
+12
-7
@@ -18,8 +18,8 @@
|
|||||||
package com.rtbishop.look4sat.presentation.entriesScreen
|
package com.rtbishop.look4sat.presentation.entriesScreen
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
import com.rtbishop.look4sat.domain.IRepository
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
import com.rtbishop.look4sat.domain.model.DataState
|
||||||
import com.rtbishop.look4sat.domain.model.SatItem
|
import com.rtbishop.look4sat.domain.model.SatItem
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -28,13 +28,13 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class EntriesViewModel @Inject constructor(
|
class EntriesViewModel @Inject constructor(
|
||||||
private val preferences: ISettingsHandler,
|
private val repository: IRepository,
|
||||||
private val repository: IDataRepository
|
private val settings: ISettings
|
||||||
) : ViewModel(), EntriesAdapter.EntriesClickListener {
|
) : ViewModel(), EntriesAdapter.EntriesClickListener {
|
||||||
|
|
||||||
private val transModes = MutableLiveData(preferences.loadModesSelection())
|
private val transModes = MutableLiveData(settings.loadModesSelection())
|
||||||
private val currentQuery = MutableLiveData(String())
|
private val currentQuery = MutableLiveData(String())
|
||||||
private val itemsFromRepo = liveData { emit(repository.getEntriesWithModes()) } as MutableLiveData
|
private val itemsFromRepo = liveData { emit(loadEntriesWithSelection()) } as MutableLiveData
|
||||||
private val itemsWithModes = transModes.switchMap { modes ->
|
private val itemsWithModes = transModes.switchMap { modes ->
|
||||||
itemsFromRepo.map { items -> filterByModes(items, modes) }
|
itemsFromRepo.map { items -> filterByModes(items, modes) }
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ class EntriesViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun saveSelectedModes(modes: List<String>) {
|
fun saveSelectedModes(modes: List<String>) {
|
||||||
transModes.value = modes
|
transModes.value = modes
|
||||||
preferences.saveModesSelection(modes)
|
settings.saveModesSelection(modes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setQuery(query: String) {
|
fun setQuery(query: String) {
|
||||||
@@ -74,6 +74,11 @@ class EntriesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun loadEntriesWithSelection(): List<SatItem> {
|
||||||
|
val selectedIds = settings.loadEntriesSelection()
|
||||||
|
return repository.getEntriesWithModes().onEach { it.isSelected = it.catnum in selectedIds }
|
||||||
|
}
|
||||||
|
|
||||||
private fun filterByModes(items: List<SatItem>, modes: List<String>): List<SatItem> {
|
private fun filterByModes(items: List<SatItem>, modes: List<String>): List<SatItem> {
|
||||||
if (modes.isEmpty()) return items
|
if (modes.isEmpty()) return items
|
||||||
return items.filter { item -> item.modes.any { mode -> mode in modes } }
|
return items.filter { item -> item.modes.any { mode -> mode in modes } }
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import androidx.recyclerview.widget.DividerItemDecoration
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.databinding.DialogModesBinding
|
import com.rtbishop.look4sat.databinding.DialogModesBinding
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.presentation.setNavResult
|
import com.rtbishop.look4sat.presentation.setNavResult
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -19,7 +19,7 @@ import javax.inject.Inject
|
|||||||
class ModesDialog : AppCompatDialogFragment(), ModesAdapter.ModesClickListener {
|
class ModesDialog : AppCompatDialogFragment(), ModesAdapter.ModesClickListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: ISettingsHandler
|
lateinit var preferences: ISettings
|
||||||
private lateinit var binding: DialogModesBinding
|
private lateinit var binding: DialogModesBinding
|
||||||
private val allModes = listOf(
|
private val allModes = listOf(
|
||||||
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
|
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.mapScreen
|
package com.rtbishop.look4sat.presentation.mapScreen
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.ColorMatrix
|
import android.graphics.ColorMatrix
|
||||||
import android.graphics.ColorMatrixColorFilter
|
import android.graphics.ColorMatrixColorFilter
|
||||||
@@ -43,17 +43,12 @@ import org.osmdroid.views.overlay.FolderOverlay
|
|||||||
import org.osmdroid.views.overlay.Marker
|
import org.osmdroid.views.overlay.Marker
|
||||||
import org.osmdroid.views.overlay.Polygon
|
import org.osmdroid.views.overlay.Polygon
|
||||||
import org.osmdroid.views.overlay.Polyline
|
import org.osmdroid.views.overlay.Polyline
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MapFragment : Fragment(R.layout.fragment_map) {
|
class MapFragment : Fragment(R.layout.fragment_map) {
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var sharedPreferences: SharedPreferences
|
|
||||||
private lateinit var binding: FragmentMapBinding
|
|
||||||
|
|
||||||
private val args: MapFragmentArgs by navArgs()
|
|
||||||
private val viewModel: MapViewModel by viewModels()
|
private val viewModel: MapViewModel by viewModels()
|
||||||
|
private val navArgs: MapFragmentArgs by navArgs()
|
||||||
private val minLat = MapView.getTileSystem().minLatitude
|
private val minLat = MapView.getTileSystem().minLatitude
|
||||||
private val maxLat = MapView.getTileSystem().maxLatitude
|
private val maxLat = MapView.getTileSystem().maxLatitude
|
||||||
private val trackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val trackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
@@ -67,6 +62,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
style = Paint.Style.FILL_AND_STROKE
|
style = Paint.Style.FILL_AND_STROKE
|
||||||
color = Color.parseColor("#26FFE082")
|
color = Color.parseColor("#26FFE082")
|
||||||
}
|
}
|
||||||
|
private lateinit var binding: FragmentMapBinding
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@@ -76,7 +72,9 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupComponents() {
|
private fun setupComponents() {
|
||||||
Configuration.getInstance().load(requireContext(), sharedPreferences)
|
val context = requireContext()
|
||||||
|
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
|
||||||
|
Configuration.getInstance().load(context, preferences)
|
||||||
binding.run {
|
binding.run {
|
||||||
mapView.apply {
|
mapView.apply {
|
||||||
setMultiTouchControls(true)
|
setMultiTouchControls(true)
|
||||||
@@ -99,15 +97,15 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
viewModel.selectDefaultSatellite(args.catNum)
|
viewModel.selectDefaultSatellite(navArgs.catNum)
|
||||||
viewModel.stationPosLiveData.observe(viewLifecycleOwner) { renderStationPos(it) }
|
viewModel.stationPos.observe(viewLifecycleOwner) { handleStationPos(it) }
|
||||||
viewModel.satPositions.observe(viewLifecycleOwner) { renderSatPositions(it) }
|
viewModel.positions.observe(viewLifecycleOwner) { handlePositions(it) }
|
||||||
viewModel.satTrack.observe(viewLifecycleOwner) { renderSatTrack(it) }
|
viewModel.track.observe(viewLifecycleOwner) { handleTrack(it) }
|
||||||
viewModel.satFootprint.observe(viewLifecycleOwner) { renderSatFootprint(it) }
|
viewModel.footprint.observe(viewLifecycleOwner) { handleFootprint(it) }
|
||||||
viewModel.mapData.observe(viewLifecycleOwner) { renderSatData(it) }
|
viewModel.mapData.observe(viewLifecycleOwner) { handleMapData(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderStationPos(stationPos: GeoPos) {
|
private fun handleStationPos(stationPos: GeoPos) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
Marker(mapView).apply {
|
Marker(mapView).apply {
|
||||||
setInfoWindow(null)
|
setInfoWindow(null)
|
||||||
@@ -120,7 +118,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSatPositions(posMap: Map<Satellite, GeoPos>) {
|
private fun handlePositions(posMap: Map<Satellite, GeoPos>) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
val markers = FolderOverlay()
|
val markers = FolderOverlay()
|
||||||
posMap.entries.forEach {
|
posMap.entries.forEach {
|
||||||
@@ -149,7 +147,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSatTrack(satTrack: List<List<GeoPos>>) {
|
private fun handleTrack(satTrack: List<List<GeoPos>>) {
|
||||||
val trackOverlay = FolderOverlay()
|
val trackOverlay = FolderOverlay()
|
||||||
satTrack.forEach { track ->
|
satTrack.forEach { track ->
|
||||||
val trackPoints = track.map { GeoPoint(it.latitude, it.longitude) }
|
val trackPoints = track.map { GeoPoint(it.latitude, it.longitude) }
|
||||||
@@ -166,7 +164,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
binding.mapView.overlays[1] = trackOverlay
|
binding.mapView.overlays[1] = trackOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSatFootprint(satFootprint: List<GeoPos>) {
|
private fun handleFootprint(satFootprint: List<GeoPos>) {
|
||||||
val footprintPoints = satFootprint.map { GeoPoint(it.latitude, it.longitude) }
|
val footprintPoints = satFootprint.map { GeoPoint(it.latitude, it.longitude) }
|
||||||
val footprintOverlay = Polygon().apply {
|
val footprintOverlay = Polygon().apply {
|
||||||
fillPaint.set(footprintPaint)
|
fillPaint.set(footprintPaint)
|
||||||
@@ -180,7 +178,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
binding.mapView.overlays[2] = footprintOverlay
|
binding.mapView.overlays[2] = footprintOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSatData(mapData: MapData) {
|
private fun handleMapData(mapData: MapData) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
mapDataName.text =
|
mapDataName.text =
|
||||||
String.format(getString(R.string.pat_osm_idName), mapData.catNum, mapData.name)
|
String.format(getString(R.string.pat_osm_idName), mapData.catNum, mapData.name)
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
package com.rtbishop.look4sat.presentation.mapScreen
|
package com.rtbishop.look4sat.presentation.mapScreen
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
import com.rtbishop.look4sat.domain.IRepository
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.QthConverter
|
import com.rtbishop.look4sat.domain.QthConverter
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||||
@@ -33,52 +33,54 @@ import kotlin.math.min
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MapViewModel @Inject constructor(
|
class MapViewModel @Inject constructor(
|
||||||
private val dataRepository: IDataRepository,
|
|
||||||
private val predictor: Predictor,
|
private val predictor: Predictor,
|
||||||
preferences: ISettingsHandler,
|
private val repository: IRepository,
|
||||||
|
private val settings: ISettings,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val stationPos = preferences.loadStationPosition()
|
private val stationPosition = settings.loadStationPosition()
|
||||||
|
private var allSatellites = listOf<Satellite>()
|
||||||
private var dataUpdateJob: Job? = null
|
private var dataUpdateJob: Job? = null
|
||||||
private var allSatList = listOf<Satellite>()
|
private var dataUpdateRate = 2000L
|
||||||
private lateinit var selectedSat: Satellite
|
private lateinit var selectedSatellite: Satellite
|
||||||
|
|
||||||
val stationPosLiveData = liveData {
|
val stationPos = liveData {
|
||||||
val osmLat = clipLat(stationPos.latitude)
|
val osmLat = clipLat(stationPosition.latitude)
|
||||||
val osmLon = clipLon(stationPos.longitude)
|
val osmLon = clipLon(stationPosition.longitude)
|
||||||
emit(GeoPos(osmLat, osmLon))
|
emit(GeoPos(osmLat, osmLon))
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _satTrack = MutableLiveData<List<List<GeoPos>>>()
|
private val _track = MutableLiveData<List<List<GeoPos>>>()
|
||||||
val satTrack: LiveData<List<List<GeoPos>>> = _satTrack
|
val track: LiveData<List<List<GeoPos>>> = _track
|
||||||
|
|
||||||
private val _satFootprint = MutableLiveData<List<GeoPos>>()
|
private val _footprint = MutableLiveData<List<GeoPos>>()
|
||||||
val satFootprint: LiveData<List<GeoPos>> = _satFootprint
|
val footprint: LiveData<List<GeoPos>> = _footprint
|
||||||
|
|
||||||
private val _satData = MutableLiveData<MapData>()
|
private val _mapData = MutableLiveData<MapData>()
|
||||||
val mapData: LiveData<MapData> = this._satData
|
val mapData: LiveData<MapData> = this._mapData
|
||||||
|
|
||||||
private val _satPositions = MutableLiveData<Map<Satellite, GeoPos>>()
|
private val _positions = MutableLiveData<Map<Satellite, GeoPos>>()
|
||||||
val satPositions: LiveData<Map<Satellite, GeoPos>> = _satPositions
|
val positions: LiveData<Map<Satellite, GeoPos>> = _positions
|
||||||
|
|
||||||
fun scrollSelection(decrement: Boolean) {
|
fun scrollSelection(decrement: Boolean) {
|
||||||
if (allSatList.isNotEmpty()) {
|
if (allSatellites.isNotEmpty()) {
|
||||||
val index = allSatList.indexOf(selectedSat)
|
val index = allSatellites.indexOf(selectedSatellite)
|
||||||
if (decrement) {
|
if (decrement) {
|
||||||
if (index > 0) selectSatellite(allSatList[index - 1])
|
if (index > 0) selectSatellite(allSatellites[index - 1])
|
||||||
else selectSatellite(allSatList[allSatList.size - 1])
|
else selectSatellite(allSatellites[allSatellites.size - 1])
|
||||||
} else {
|
} else {
|
||||||
if (index < allSatList.size - 1) selectSatellite(allSatList[index + 1])
|
if (index < allSatellites.size - 1) selectSatellite(allSatellites[index + 1])
|
||||||
else selectSatellite(allSatList[0])
|
else selectSatellite(allSatellites[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectDefaultSatellite(catnum: Int) {
|
fun selectDefaultSatellite(catnum: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
dataRepository.getSelectedEntries().also { satellites ->
|
val selectedIds = settings.loadEntriesSelection()
|
||||||
|
repository.getEntriesWithIds(selectedIds).also { satellites ->
|
||||||
if (satellites.isNotEmpty()) {
|
if (satellites.isNotEmpty()) {
|
||||||
allSatList = satellites
|
allSatellites = satellites
|
||||||
if (catnum == -1) {
|
if (catnum == -1) {
|
||||||
selectSatellite(satellites.first())
|
selectSatellite(satellites.first())
|
||||||
} else {
|
} else {
|
||||||
@@ -89,18 +91,18 @@ class MapViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectSatellite(satellite: Satellite, updateFreq: Long = 2000) {
|
fun selectSatellite(satellite: Satellite, updateFreq: Long = dataUpdateRate) {
|
||||||
selectedSat = satellite
|
selectedSatellite = satellite
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
dataUpdateJob?.cancelAndJoin()
|
dataUpdateJob?.cancelAndJoin()
|
||||||
dataUpdateJob = launch {
|
dataUpdateJob = launch {
|
||||||
val dateNow = Date()
|
val dateNow = Date()
|
||||||
getSatTrack(selectedSat, stationPos, dateNow)
|
getSatTrack(selectedSatellite, stationPosition, dateNow)
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
dateNow.time = System.currentTimeMillis()
|
dateNow.time = System.currentTimeMillis()
|
||||||
getPositions(allSatList, stationPos, dateNow)
|
getPositions(allSatellites, stationPosition, dateNow)
|
||||||
getSatFootprint(selectedSat, stationPos, dateNow)
|
getSatFootprint(selectedSatellite, stationPosition, dateNow)
|
||||||
getSatData(selectedSat, stationPos, dateNow)
|
getSatData(selectedSatellite, stationPosition, dateNow)
|
||||||
delay(updateFreq)
|
delay(updateFreq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +135,7 @@ class MapViewModel @Inject constructor(
|
|||||||
currentTrack.add(currentPosition)
|
currentTrack.add(currentPosition)
|
||||||
}
|
}
|
||||||
satTracks.add(currentTrack)
|
satTracks.add(currentTrack)
|
||||||
_satTrack.postValue(satTracks)
|
_track.postValue(satTracks)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
private suspend fun getPositions(satellites: List<Satellite>, pos: GeoPos, date: Date) {
|
||||||
@@ -144,7 +146,7 @@ class MapViewModel @Inject constructor(
|
|||||||
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
|
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
|
||||||
positions[satellite] = GeoPos(osmLat, osmLon)
|
positions[satellite] = GeoPos(osmLat, osmLon)
|
||||||
}
|
}
|
||||||
_satPositions.postValue(positions)
|
_positions.postValue(positions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
private suspend fun getSatFootprint(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||||
@@ -154,7 +156,7 @@ class MapViewModel @Inject constructor(
|
|||||||
val osmLon = clipLon(rangePos.longitude)
|
val osmLon = clipLon(rangePos.longitude)
|
||||||
GeoPos(osmLat, osmLon)
|
GeoPos(osmLat, osmLon)
|
||||||
}
|
}
|
||||||
_satFootprint.postValue(satFootprint)
|
_footprint.postValue(satFootprint)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getSatData(satellite: Satellite, pos: GeoPos, date: Date) {
|
private suspend fun getSatData(satellite: Satellite, pos: GeoPos, date: Date) {
|
||||||
@@ -167,7 +169,7 @@ class MapViewModel @Inject constructor(
|
|||||||
satellite, satellite.data.catnum, satellite.data.name, satPos.distance,
|
satellite, satellite.data.catnum, satellite.data.name, satPos.distance,
|
||||||
satPos.altitude, satPos.getOrbitalVelocity(), qthLoc, osmPos
|
satPos.altitude, satPos.getOrbitalVelocity(), qthLoc, osmPos
|
||||||
)
|
)
|
||||||
_satData.postValue(satData)
|
_mapData.postValue(satData)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clipLat(latitude: Double): Double {
|
private fun clipLat(latitude: Double): Double {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import android.view.ViewGroup
|
|||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.databinding.DialogFilterBinding
|
import com.rtbishop.look4sat.databinding.DialogFilterBinding
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.presentation.setNavResult
|
import com.rtbishop.look4sat.presentation.setNavResult
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -17,7 +17,7 @@ import javax.inject.Inject
|
|||||||
class FilterDialog : AppCompatDialogFragment() {
|
class FilterDialog : AppCompatDialogFragment() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: ISettingsHandler
|
lateinit var preferences: ISettings
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.dialog_filter, group, false)
|
return inflater.inflate(R.layout.dialog_filter, group, false)
|
||||||
|
|||||||
@@ -18,9 +18,11 @@
|
|||||||
package com.rtbishop.look4sat.presentation.passesScreen
|
package com.rtbishop.look4sat.presentation.passesScreen
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.LinearInterpolator
|
import android.view.animation.LinearInterpolator
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
@@ -34,7 +36,6 @@ import com.rtbishop.look4sat.domain.model.DataState
|
|||||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||||
import com.rtbishop.look4sat.domain.toTimerString
|
import com.rtbishop.look4sat.domain.toTimerString
|
||||||
import com.rtbishop.look4sat.presentation.getNavResult
|
import com.rtbishop.look4sat.presentation.getNavResult
|
||||||
import com.rtbishop.look4sat.presentation.toast
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@@ -83,8 +84,8 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
|||||||
viewModel.passes.observe(viewLifecycleOwner) { passesResult ->
|
viewModel.passes.observe(viewLifecycleOwner) { passesResult ->
|
||||||
handleNewPasses(passesResult, adapter)
|
handleNewPasses(passesResult, adapter)
|
||||||
}
|
}
|
||||||
viewModel.entries.observe(viewLifecycleOwner) { number ->
|
viewModel.entriesTotal.observe(viewLifecycleOwner) { number ->
|
||||||
handleEntriesNumber(number)
|
handleEntriesTotal(number)
|
||||||
}
|
}
|
||||||
getNavResult<Pair<Int, Double>>(R.id.nav_passes, "prefs") { prefs ->
|
getNavResult<Pair<Int, Double>>(R.id.nav_passes, "prefs") { prefs ->
|
||||||
viewModel.calculatePasses(prefs.first, prefs.second)
|
viewModel.calculatePasses(prefs.first, prefs.second)
|
||||||
@@ -103,7 +104,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEntriesNumber(number: Int) {
|
private fun handleEntriesTotal(number: Int) {
|
||||||
if (number > 0) {
|
if (number > 0) {
|
||||||
binding.passesFab.setOnClickListener { navigateToEntries() }
|
binding.passesFab.setOnClickListener { navigateToEntries() }
|
||||||
} else {
|
} else {
|
||||||
@@ -190,4 +191,8 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
|||||||
findNavController().navigate(action)
|
findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Context.toast(text: String, duration: Int = Toast.LENGTH_SHORT) {
|
||||||
|
Toast.makeText(this, text, duration).apply { show() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+9
-7
@@ -18,8 +18,8 @@
|
|||||||
package com.rtbishop.look4sat.presentation.passesScreen
|
package com.rtbishop.look4sat.presentation.passesScreen
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
import com.rtbishop.look4sat.domain.IRepository
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
import com.rtbishop.look4sat.domain.model.DataState
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||||
@@ -30,14 +30,14 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class PassesViewModel @Inject constructor(
|
class PassesViewModel @Inject constructor(
|
||||||
private val predictor: Predictor,
|
private val predictor: Predictor,
|
||||||
private val settings: ISettingsHandler,
|
private val repository: IRepository,
|
||||||
private val repository: IDataRepository
|
private val settings: ISettings
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private var passesProcessing: Job? = null
|
private var passesProcessing: Job? = null
|
||||||
private val _passes = MutableLiveData<DataState<List<SatPass>>>(DataState.Loading)
|
private val _passes = MutableLiveData<DataState<List<SatPass>>>(DataState.Loading)
|
||||||
val passes: LiveData<DataState<List<SatPass>>> = _passes
|
val passes: LiveData<DataState<List<SatPass>>> = _passes
|
||||||
val entries: LiveData<Int> = repository.getEntriesNumber().asLiveData()
|
val entriesTotal: LiveData<Int> = repository.getEntriesTotal().asLiveData()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -53,6 +53,7 @@ class PassesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
calculatePasses()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shouldUseUTC() = settings.getUseUTC()
|
fun shouldUseUTC() = settings.getUseUTC()
|
||||||
@@ -66,11 +67,12 @@ class PassesViewModel @Inject constructor(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_passes.postValue(DataState.Loading)
|
_passes.postValue(DataState.Loading)
|
||||||
passesProcessing?.cancelAndJoin()
|
passesProcessing?.cancelAndJoin()
|
||||||
selection?.let { items -> repository.setEntriesSelection(items) }
|
selection?.let { items -> settings.saveEntriesSelection(items) }
|
||||||
settings.setHoursAhead(hoursAhead)
|
settings.setHoursAhead(hoursAhead)
|
||||||
settings.setMinElevation(minElevation)
|
settings.setMinElevation(minElevation)
|
||||||
val satellites = repository.getSelectedEntries()
|
|
||||||
val stationPos = settings.loadStationPosition()
|
val stationPos = settings.loadStationPosition()
|
||||||
|
val selectedIds = settings.loadEntriesSelection()
|
||||||
|
val satellites = repository.getEntriesWithIds(selectedIds)
|
||||||
predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation)
|
predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,31 +27,27 @@ import androidx.recyclerview.widget.DividerItemDecoration
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.databinding.FragmentRadarBinding
|
import com.rtbishop.look4sat.databinding.FragmentRadarBinding
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||||
import com.rtbishop.look4sat.domain.toTimerString
|
import com.rtbishop.look4sat.domain.toTimerString
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class RadarFragment : Fragment(R.layout.fragment_radar) {
|
class RadarFragment : Fragment(R.layout.fragment_radar) {
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var preferences: ISettingsHandler
|
|
||||||
private lateinit var binding: FragmentRadarBinding
|
|
||||||
private val args: RadarFragmentArgs by navArgs()
|
|
||||||
private val viewModel: RadarViewModel by viewModels()
|
private val viewModel: RadarViewModel by viewModels()
|
||||||
private var radarView: RadarView? = null
|
private val navArgs: RadarFragmentArgs by navArgs()
|
||||||
|
private lateinit var binding: FragmentRadarBinding
|
||||||
|
private lateinit var radarView: RadarView
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding = FragmentRadarBinding.bind(view)
|
binding = FragmentRadarBinding.bind(view)
|
||||||
setupViews()
|
setupComponents()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupViews() {
|
private fun setupComponents() {
|
||||||
val context = requireContext()
|
val context = requireContext()
|
||||||
val adapter = RadioAdapter()
|
val adapter = RadioAdapter()
|
||||||
val layoutManager = LinearLayoutManager(context)
|
val layoutManager = LinearLayoutManager(context)
|
||||||
@@ -69,15 +65,15 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers(radioAdapter: RadioAdapter) {
|
private fun setupObservers(radioAdapter: RadioAdapter) {
|
||||||
viewModel.getPass(args.catNum, args.aosTime).observe(viewLifecycleOwner) { pass ->
|
viewModel.getPass(navArgs.catNum, navArgs.aosTime).observe(viewLifecycleOwner) { pass ->
|
||||||
radarView = RadarView(requireContext()).apply {
|
radarView = RadarView(requireContext()).apply {
|
||||||
setShowAim(preferences.getUseCompass())
|
setShowAim(viewModel.getUseCompass())
|
||||||
setScanning(preferences.getShowSweep())
|
setScanning(viewModel.getShowSweep())
|
||||||
}
|
}
|
||||||
binding.radarCard.addView(radarView)
|
binding.radarCard.addView(radarView)
|
||||||
viewModel.radarData.observe(viewLifecycleOwner) { passData ->
|
viewModel.radarData.observe(viewLifecycleOwner) { passData ->
|
||||||
radarView?.setPosition(passData.satPos)
|
radarView.setPosition(passData.satPos)
|
||||||
radarView?.setPositions(passData.satTrack)
|
radarView.setPositions(passData.satTrack)
|
||||||
setPassText(pass, passData.satPos)
|
setPassText(pass, passData.satPos)
|
||||||
}
|
}
|
||||||
viewModel.transmitters.observe(viewLifecycleOwner) { list ->
|
viewModel.transmitters.observe(viewLifecycleOwner) { list ->
|
||||||
@@ -87,16 +83,15 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|||||||
} else {
|
} else {
|
||||||
binding.radarRecyclerMsg.text = getString(R.string.trans_no_data)
|
binding.radarRecyclerMsg.text = getString(R.string.trans_no_data)
|
||||||
}
|
}
|
||||||
radarView?.invalidate()
|
radarView.invalidate()
|
||||||
}
|
}
|
||||||
viewModel.orientation.observe(viewLifecycleOwner) { orientation ->
|
viewModel.orientation.observe(viewLifecycleOwner) { value ->
|
||||||
radarView?.setOrientation(
|
radarView.setOrientation(value.first, value.second, value.third)
|
||||||
orientation.first,
|
|
||||||
orientation.second,
|
|
||||||
orientation.third
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
binding.radarBack.setOnClickListener { findNavController().navigateUp() }
|
||||||
binding.radarMap.setOnClickListener { navigateToMap(pass.catNum) }
|
binding.radarMap.setOnClickListener { navigateToMap(pass.catNum) }
|
||||||
|
binding.radarBtnNotify.isEnabled = false
|
||||||
|
binding.radarBtnSettings.setOnClickListener { navigateToSettings() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,11 +101,12 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|||||||
val radarElev = getString(R.string.radar_el_value)
|
val radarElev = getString(R.string.radar_el_value)
|
||||||
val radarAlt = getString(R.string.radar_alt_value)
|
val radarAlt = getString(R.string.radar_alt_value)
|
||||||
val radarDist = getString(R.string.radar_dist_value)
|
val radarDist = getString(R.string.radar_dist_value)
|
||||||
|
val radarId = getString(R.string.radar_sat_id)
|
||||||
binding.radarAzValue.text = String.format(radarAzim, Math.toDegrees(satPos.azimuth))
|
binding.radarAzValue.text = String.format(radarAzim, Math.toDegrees(satPos.azimuth))
|
||||||
binding.radarElValue.text = String.format(radarElev, Math.toDegrees(satPos.elevation))
|
binding.radarElValue.text = String.format(radarElev, Math.toDegrees(satPos.elevation))
|
||||||
binding.radarAltValue.text = String.format(radarAlt, satPos.altitude)
|
binding.radarAltValue.text = String.format(radarAlt, satPos.altitude)
|
||||||
binding.radarDstValue.text = String.format(radarDist, satPos.distance)
|
binding.radarDstValue.text = String.format(radarDist, satPos.distance)
|
||||||
// binding.radarName.text = satPass.name
|
binding.radarSatId.text = String.format(radarId, satPass.catNum)
|
||||||
|
|
||||||
if (!satPass.isDeepSpace) {
|
if (!satPass.isDeepSpace) {
|
||||||
if (timeNow < satPass.aosTime) {
|
if (timeNow < satPass.aosTime) {
|
||||||
@@ -134,6 +130,11 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
|
|||||||
findNavController().navigate(direction)
|
findNavController().navigate(direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun navigateToSettings() {
|
||||||
|
val direction = RadarFragmentDirections.actionGlobalSettingsFragment()
|
||||||
|
findNavController().navigate(direction)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
viewModel.enableSensor()
|
viewModel.enableSensor()
|
||||||
|
|||||||
+18
-14
@@ -19,9 +19,9 @@ package com.rtbishop.look4sat.presentation.radarScreen
|
|||||||
|
|
||||||
import android.hardware.GeomagneticField
|
import android.hardware.GeomagneticField
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.domain.DataReporter
|
import com.rtbishop.look4sat.domain.DataReporter
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.IRepository
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||||
@@ -39,13 +39,13 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class RadarViewModel @Inject constructor(
|
class RadarViewModel @Inject constructor(
|
||||||
private val orientationHandler: OrientationHandler,
|
private val orientationHandler: OrientationHandler,
|
||||||
private val preferences: ISettingsHandler,
|
private val reporter: DataReporter,
|
||||||
private val predictor: Predictor,
|
private val predictor: Predictor,
|
||||||
private val dataRepository: IDataRepository,
|
private val repository: IRepository,
|
||||||
private val dataReporter: DataReporter
|
private val settings: ISettings
|
||||||
) : ViewModel(), OrientationHandler.OrientationListener {
|
) : ViewModel(), OrientationHandler.OrientationListener {
|
||||||
|
|
||||||
private val stationPos = preferences.loadStationPosition()
|
private val stationPos = settings.loadStationPosition()
|
||||||
private val _passData = MutableLiveData<RadarData>()
|
private val _passData = MutableLiveData<RadarData>()
|
||||||
private val _transmitters = MutableLiveData<List<SatRadio>>()
|
private val _transmitters = MutableLiveData<List<SatRadio>>()
|
||||||
private val _orientation = MutableLiveData<Triple<Float, Float, Float>>()
|
private val _orientation = MutableLiveData<Triple<Float, Float, Float>>()
|
||||||
@@ -55,7 +55,7 @@ class RadarViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun getPass(catNum: Int, aosTime: Long) = liveData {
|
fun getPass(catNum: Int, aosTime: Long) = liveData {
|
||||||
predictor.calculatedPasses.collect { passes ->
|
predictor.calculatedPasses.collect { passes ->
|
||||||
val pass = passes.find { it.catNum == catNum && it.aosTime == aosTime }
|
val pass = passes.find { pass -> pass.catNum == catNum && pass.aosTime == aosTime }
|
||||||
pass?.let { satPass ->
|
pass?.let { satPass ->
|
||||||
emit(satPass)
|
emit(satPass)
|
||||||
sendPassData(satPass)
|
sendPassData(satPass)
|
||||||
@@ -65,13 +65,17 @@ class RadarViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun enableSensor() {
|
fun enableSensor() {
|
||||||
if (preferences.getUseCompass()) orientationHandler.startListening(this)
|
if (settings.getUseCompass()) orientationHandler.startListening(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disableSensor() {
|
fun disableSensor() {
|
||||||
if (preferences.getUseCompass()) orientationHandler.stopListening()
|
if (settings.getUseCompass()) orientationHandler.stopListening()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUseCompass(): Boolean = settings.getUseCompass()
|
||||||
|
|
||||||
|
fun getShowSweep(): Boolean = settings.getShowSweep()
|
||||||
|
|
||||||
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
|
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
|
||||||
_orientation.value = Triple(azimuth + getMagDeclination(stationPos), pitch, roll)
|
_orientation.value = Triple(azimuth + getMagDeclination(stationPos), pitch, roll)
|
||||||
}
|
}
|
||||||
@@ -92,12 +96,12 @@ class RadarViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date().time)
|
val satPos = predictor.getSatPos(satPass.satellite, stationPos, Date().time)
|
||||||
if (preferences.getRotatorEnabled()) {
|
if (settings.getRotatorEnabled()) {
|
||||||
val server = preferences.getRotatorServer()
|
val server = settings.getRotatorServer()
|
||||||
val port = preferences.getRotatorPort().toInt()
|
val port = settings.getRotatorPort().toInt()
|
||||||
val azimuth = Math.toDegrees(satPos.azimuth).round(1)
|
val azimuth = Math.toDegrees(satPos.azimuth).round(1)
|
||||||
val elevation = Math.toDegrees(satPos.elevation).round(1)
|
val elevation = Math.toDegrees(satPos.elevation).round(1)
|
||||||
dataReporter.reportRotation(server, port, azimuth, elevation)
|
reporter.reportRotation(server, port, azimuth, elevation)
|
||||||
}
|
}
|
||||||
_passData.postValue(RadarData(satPos, satTrack))
|
_passData.postValue(RadarData(satPos, satTrack))
|
||||||
delay(1000)
|
delay(1000)
|
||||||
@@ -107,7 +111,7 @@ class RadarViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun processTransmitters(pass: SatPass) {
|
private fun processTransmitters(pass: SatPass) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val transmitters = dataRepository.getRadios(pass.catNum)
|
val transmitters = repository.getRadiosWithId(pass.catNum)
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val time = System.currentTimeMillis()
|
val time = System.currentTimeMillis()
|
||||||
val list = predictor.processRadios(pass.satellite, stationPos, transmitters, time)
|
val list = predictor.processRadios(pass.satellite, stationPos, transmitters, time)
|
||||||
|
|||||||
+2
-2
@@ -7,8 +7,8 @@ import android.view.ViewGroup
|
|||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.databinding.DialogLocationBinding
|
import com.rtbishop.look4sat.databinding.DialogLocationBinding
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.presentation.setNavResult
|
import com.rtbishop.look4sat.presentation.setNavResult
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -17,7 +17,7 @@ import javax.inject.Inject
|
|||||||
class LocationDialog : AppCompatDialogFragment() {
|
class LocationDialog : AppCompatDialogFragment() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: ISettingsHandler
|
lateinit var preferences: ISettings
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.dialog_location, group, false)
|
return inflater.inflate(R.layout.dialog_location, group, false)
|
||||||
|
|||||||
+2
-2
@@ -7,8 +7,8 @@ import android.view.ViewGroup
|
|||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.databinding.DialogLocatorBinding
|
import com.rtbishop.look4sat.databinding.DialogLocatorBinding
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.presentation.setNavResult
|
import com.rtbishop.look4sat.presentation.setNavResult
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -17,7 +17,7 @@ import javax.inject.Inject
|
|||||||
class LocatorDialog : AppCompatDialogFragment() {
|
class LocatorDialog : AppCompatDialogFragment() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: ISettingsHandler
|
lateinit var preferences: ISettings
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.dialog_locator, group, false)
|
return inflater.inflate(R.layout.dialog_locator, group, false)
|
||||||
|
|||||||
+5
-5
@@ -44,7 +44,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||||
|
|
||||||
private lateinit var binding: FragmentSettingsBinding
|
|
||||||
private val viewModel: SettingsViewModel by viewModels()
|
private val viewModel: SettingsViewModel by viewModels()
|
||||||
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
@@ -60,15 +59,16 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
|||||||
private val contentRequest = registerForActivityResult(contentContract) { uri ->
|
private val contentRequest = registerForActivityResult(contentContract) { uri ->
|
||||||
uri?.let { viewModel.updateDataFromFile(uri.toString()) }
|
uri?.let { viewModel.updateDataFromFile(uri.toString()) }
|
||||||
}
|
}
|
||||||
|
private lateinit var binding: FragmentSettingsBinding
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding = FragmentSettingsBinding.bind(view)
|
binding = FragmentSettingsBinding.bind(view)
|
||||||
setupViews()
|
setupComponents()
|
||||||
setupObservers()
|
setupObservers()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupViews() {
|
private fun setupComponents() {
|
||||||
binding.settingsScroll.apply {
|
binding.settingsScroll.apply {
|
||||||
setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, y, _, newY ->
|
setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, y, _, newY ->
|
||||||
if (y > newY) binding.settingsFab.hide() else binding.settingsFab.show()
|
if (y > newY) binding.settingsFab.hide() else binding.settingsFab.show()
|
||||||
@@ -134,11 +134,11 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
|||||||
findNavController().navigate(action)
|
findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
binding.settingsData.dataBtnClear.setOnClickListener { viewModel.clearData() }
|
binding.settingsData.dataBtnClear.setOnClickListener { viewModel.clearData() }
|
||||||
viewModel.entries.observe(viewLifecycleOwner) { number ->
|
viewModel.entriesTotal.observe(viewLifecycleOwner) { number ->
|
||||||
val entriesFormat = getString(R.string.fmt_entries)
|
val entriesFormat = getString(R.string.fmt_entries)
|
||||||
binding.settingsData.dataEntries.text = String.format(entriesFormat, number)
|
binding.settingsData.dataEntries.text = String.format(entriesFormat, number)
|
||||||
}
|
}
|
||||||
viewModel.radios.observe(viewLifecycleOwner) { number ->
|
viewModel.radiosTotal.observe(viewLifecycleOwner) { number ->
|
||||||
val radiosFormat = getString(R.string.fmt_radios)
|
val radiosFormat = getString(R.string.fmt_radios)
|
||||||
binding.settingsData.dataRadios.text = String.format(radiosFormat, number)
|
binding.settingsData.dataRadios.text = String.format(radiosFormat, number)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-7
@@ -2,9 +2,9 @@ package com.rtbishop.look4sat.presentation.settingsScreen
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import com.rtbishop.look4sat.data.ISettingsHandler
|
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
|
||||||
import com.rtbishop.look4sat.domain.ILocationHandler
|
import com.rtbishop.look4sat.domain.ILocationHandler
|
||||||
|
import com.rtbishop.look4sat.domain.IRepository
|
||||||
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
import com.rtbishop.look4sat.domain.model.DataState
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -13,19 +13,20 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SettingsViewModel @Inject constructor(
|
class SettingsViewModel @Inject constructor(
|
||||||
private val settings: ISettingsHandler,
|
private val locationHandler: ILocationHandler,
|
||||||
private val repository: IDataRepository,
|
private val repository: IRepository,
|
||||||
private val locationHandler: ILocationHandler
|
private val settings: ISettings
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val entries = repository.getEntriesNumber().asLiveData()
|
val entriesTotal = repository.getEntriesTotal().asLiveData()
|
||||||
val radios = repository.getRadiosNumber().asLiveData()
|
val radiosTotal = repository.getRadiosTotal().asLiveData()
|
||||||
|
|
||||||
fun updateDataFromFile(uri: String) {
|
fun updateDataFromFile(uri: String) {
|
||||||
repository.updateFromFile(uri)
|
repository.updateFromFile(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateDataFromWeb(sources: List<String>) {
|
fun updateDataFromWeb(sources: List<String>) {
|
||||||
|
settings.saveDataSources(sources)
|
||||||
repository.updateFromWeb(sources)
|
repository.updateFromWeb(sources)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -27,7 +27,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.databinding.DialogSourcesBinding
|
import com.rtbishop.look4sat.databinding.DialogSourcesBinding
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
import com.rtbishop.look4sat.domain.ISettings
|
||||||
import com.rtbishop.look4sat.framework.model.DataSource
|
import com.rtbishop.look4sat.framework.model.DataSource
|
||||||
import com.rtbishop.look4sat.presentation.setNavResult
|
import com.rtbishop.look4sat.presentation.setNavResult
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@@ -37,7 +37,7 @@ import javax.inject.Inject
|
|||||||
class SourcesDialog : AppCompatDialogFragment(), SourcesAdapter.SourcesClickListener {
|
class SourcesDialog : AppCompatDialogFragment(), SourcesAdapter.SourcesClickListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var repository: IDataRepository
|
lateinit var settings: ISettings
|
||||||
private lateinit var binding: DialogSourcesBinding
|
private lateinit var binding: DialogSourcesBinding
|
||||||
private lateinit var sourcesAdapter: SourcesAdapter
|
private lateinit var sourcesAdapter: SourcesAdapter
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ class SourcesDialog : AppCompatDialogFragment(), SourcesAdapter.SourcesClickList
|
|||||||
WindowManager.LayoutParams.WRAP_CONTENT
|
WindowManager.LayoutParams.WRAP_CONTENT
|
||||||
)
|
)
|
||||||
lifecycleScope.launchWhenResumed {
|
lifecycleScope.launchWhenResumed {
|
||||||
val sources = repository.getDataSources().map { url -> DataSource(url) }
|
val sources = settings.loadDataSources().map { url -> DataSource(url) }
|
||||||
val layoutManager = LinearLayoutManager(requireContext())
|
val layoutManager = LinearLayoutManager(requireContext())
|
||||||
sourcesAdapter = SourcesAdapter(this@SourcesDialog).apply { submitList(sources) }
|
sourcesAdapter = SourcesAdapter(this@SourcesDialog).apply { submitList(sources) }
|
||||||
binding.run {
|
binding.run {
|
||||||
|
|||||||
@@ -174,20 +174,21 @@
|
|||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/radar_prev_btn"
|
android:id="@+id/radar_btn_notify"
|
||||||
style="@style/NormalButton"
|
style="@style/NormalButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_marginStart="@dimen/button_margin_side"
|
android:layout_marginStart="@dimen/button_margin_side"
|
||||||
android:text="@string/btn_notify"
|
android:text="@string/btn_notify"
|
||||||
|
android:textColor="@color/textDisabled"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/radar_coordinator"
|
app:layout_constraintBottom_toBottomOf="@+id/radar_coordinator"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/radar_data_name"
|
app:layout_constraintEnd_toStartOf="@+id/radar_sat_id"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
app:layout_constraintStart_toEndOf="@+id/radar_coordinator"
|
app:layout_constraintStart_toEndOf="@+id/radar_coordinator"
|
||||||
app:layout_constraintStart_toStartOf="@+id/radar_coordinator"
|
app:layout_constraintStart_toStartOf="@+id/radar_coordinator"
|
||||||
app:layout_constraintTop_toTopOf="@+id/radar_coordinator" />
|
app:layout_constraintTop_toTopOf="@+id/radar_coordinator" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/radar_data_name"
|
android:id="@+id/radar_sat_id"
|
||||||
style="@style/WorldMapText"
|
style="@style/WorldMapText"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -196,20 +197,20 @@
|
|||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:text="@string/pat_osm_idName"
|
android:text="@string/radar_sat_id"
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_prev_btn"
|
app:layout_constraintBaseline_toBaselineOf="@+id/radar_btn_notify"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/radar_next_btn"
|
app:layout_constraintEnd_toStartOf="@+id/radar_btn_settings"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
app:layout_constraintStart_toEndOf="@+id/radar_prev_btn" />
|
app:layout_constraintStart_toEndOf="@+id/radar_btn_notify" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/radar_next_btn"
|
android:id="@+id/radar_btn_settings"
|
||||||
style="@style/NormalButton"
|
style="@style/NormalButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_marginEnd="@dimen/button_margin_side"
|
android:layout_marginEnd="@dimen/button_margin_side"
|
||||||
android:text="@string/btn_settings"
|
android:text="@string/btn_settings"
|
||||||
app:layout_constraintBaseline_toBaselineOf="@+id/radar_prev_btn"
|
app:layout_constraintBaseline_toBaselineOf="@+id/radar_btn_notify"
|
||||||
app:layout_constraintEnd_toEndOf="@+id/radar_coordinator"
|
app:layout_constraintEnd_toEndOf="@+id/radar_coordinator"
|
||||||
app:layout_constraintStart_toEndOf="@+id/radar_data_name" />
|
app:layout_constraintStart_toEndOf="@+id/radar_sat_id" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
android:id="@+id/mode_checkbox"
|
android:id="@+id/mode_checkbox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="41dp"
|
android:layout_height="41dp"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="6dp"
|
||||||
android:background="@color/cardRegular"
|
android:background="@color/cardRegular"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
|
|||||||
@@ -72,6 +72,8 @@
|
|||||||
<string name="radar_alt_value">%.0f km</string>
|
<string name="radar_alt_value">%.0f km</string>
|
||||||
<string name="radar_dist_text">Дальн.</string>
|
<string name="radar_dist_text">Дальн.</string>
|
||||||
<string name="radar_dist_value">%.0f km</string>
|
<string name="radar_dist_value">%.0f km</string>
|
||||||
|
<string name="radar_sat_id">Id: %d</string>
|
||||||
|
<string name="radar_sat_name">%s</string>
|
||||||
|
|
||||||
<string name="pat_azimuth">Азимут: %.1f°</string>
|
<string name="pat_azimuth">Азимут: %.1f°</string>
|
||||||
<string name="pat_elevation">Элевация: %.1f°</string>
|
<string name="pat_elevation">Элевация: %.1f°</string>
|
||||||
|
|||||||
@@ -84,6 +84,8 @@
|
|||||||
<string name="radar_alt_value">%.0f km</string>
|
<string name="radar_alt_value">%.0f km</string>
|
||||||
<string name="radar_dist_text">Distance</string>
|
<string name="radar_dist_text">Distance</string>
|
||||||
<string name="radar_dist_value">%.0f km</string>
|
<string name="radar_dist_value">%.0f km</string>
|
||||||
|
<string name="radar_sat_id">Id: %d</string>
|
||||||
|
<string name="radar_sat_name">%s</string>
|
||||||
|
|
||||||
<string name="pat_azimuth">Azimuth: %.1f°</string>
|
<string name="pat_azimuth">Azimuth: %.1f°</string>
|
||||||
<string name="pat_elevation">Elevation: %.1f°</string>
|
<string name="pat_elevation">Elevation: %.1f°</string>
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.domain
|
package com.rtbishop.look4sat.data
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
import com.rtbishop.look4sat.domain.predict.OrbitalData
|
||||||
+4
-2
@@ -19,9 +19,11 @@ package com.rtbishop.look4sat.data
|
|||||||
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
interface IRemoteSource {
|
interface IProvider {
|
||||||
|
|
||||||
val radioApi: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
val radioApi: String get() = "https://db.satnogs.org/api/transmitters/?format=json"
|
||||||
|
|
||||||
suspend fun getFileStream(url: String): InputStream?
|
suspend fun getLocalFileStream(uri: String): InputStream?
|
||||||
|
|
||||||
|
suspend fun getRemoteFileStream(url: String): InputStream?
|
||||||
}
|
}
|
||||||
+5
-16
@@ -22,33 +22,22 @@ import com.rtbishop.look4sat.domain.model.SatItem
|
|||||||
import com.rtbishop.look4sat.domain.model.SatRadio
|
import com.rtbishop.look4sat.domain.model.SatRadio
|
||||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
interface ILocalSource {
|
interface IStorage {
|
||||||
|
|
||||||
fun getEntriesNumber(): Flow<Int>
|
fun getEntriesTotal(): Flow<Int>
|
||||||
|
|
||||||
fun getRadiosNumber(): Flow<Int>
|
fun getRadiosTotal(): Flow<Int>
|
||||||
|
|
||||||
suspend fun getEntriesWithModes(): List<SatItem>
|
suspend fun getEntriesWithModes(): List<SatItem>
|
||||||
|
|
||||||
suspend fun getSelectedEntries(): List<Satellite>
|
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
||||||
|
|
||||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||||
|
|
||||||
suspend fun getFileStream(uri: String): InputStream?
|
|
||||||
|
|
||||||
suspend fun insertEntries(entries: List<SatEntry>)
|
suspend fun insertEntries(entries: List<SatEntry>)
|
||||||
|
|
||||||
suspend fun insertRadios(radios: List<SatRadio>)
|
suspend fun insertRadios(radios: List<SatRadio>)
|
||||||
|
|
||||||
suspend fun clearAllData()
|
suspend fun clearAllData()
|
||||||
|
|
||||||
suspend fun getDataSources(): List<String>
|
|
||||||
|
|
||||||
suspend fun setDataSources(sources: List<String>)
|
|
||||||
|
|
||||||
suspend fun getEntriesSelection(): List<Int>
|
|
||||||
|
|
||||||
suspend fun setEntriesSelection(catnums: List<Int>)
|
|
||||||
}
|
}
|
||||||
+30
-40
@@ -17,8 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.data
|
package com.rtbishop.look4sat.data
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.DataParser
|
import com.rtbishop.look4sat.domain.IRepository
|
||||||
import com.rtbishop.look4sat.domain.IDataRepository
|
|
||||||
import com.rtbishop.look4sat.domain.model.DataState
|
import com.rtbishop.look4sat.domain.model.DataState
|
||||||
import com.rtbishop.look4sat.domain.model.SatEntry
|
import com.rtbishop.look4sat.domain.model.SatEntry
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@@ -27,12 +26,12 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
class DataRepository(
|
class Repository(
|
||||||
private val dataParser: DataParser,
|
private val dataParser: DataParser,
|
||||||
private val localSource: ILocalSource,
|
private val storage: IStorage,
|
||||||
private val remoteSource: IRemoteSource,
|
private val provider: IProvider,
|
||||||
private val repositoryScope: CoroutineScope
|
private val repoScope: CoroutineScope
|
||||||
) : IDataRepository {
|
) : IRepository {
|
||||||
|
|
||||||
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
||||||
_updateState.value = DataState.Error(exception.message)
|
_updateState.value = DataState.Error(exception.message)
|
||||||
@@ -41,42 +40,35 @@ class DataRepository(
|
|||||||
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
|
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
|
||||||
override val updateState: StateFlow<DataState<Long>> = _updateState
|
override val updateState: StateFlow<DataState<Long>> = _updateState
|
||||||
|
|
||||||
override fun setUpdateStateHandled() {
|
override fun getEntriesTotal() = storage.getEntriesTotal()
|
||||||
_updateState.value = DataState.Handled
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getEntriesNumber() = localSource.getEntriesNumber()
|
override fun getRadiosTotal() = storage.getRadiosTotal()
|
||||||
|
|
||||||
override fun getRadiosNumber() = localSource.getRadiosNumber()
|
override suspend fun getEntriesWithModes() = storage.getEntriesWithModes()
|
||||||
|
|
||||||
override suspend fun getEntriesWithModes() = localSource.getEntriesWithModes()
|
override suspend fun getEntriesWithIds(ids: List<Int>) = storage.getEntriesWithIds(ids)
|
||||||
|
|
||||||
override suspend fun getSelectedEntries() = localSource.getSelectedEntries()
|
override suspend fun getRadiosWithId(id: Int) = storage.getRadiosWithId(id)
|
||||||
|
|
||||||
override suspend fun getRadios(catnum: Int) = localSource.getRadios(catnum)
|
|
||||||
|
|
||||||
override fun updateFromFile(uri: String) {
|
override fun updateFromFile(uri: String) {
|
||||||
repositoryScope.launch(exceptionHandler) {
|
repoScope.launch(exceptionHandler) {
|
||||||
_updateState.value = DataState.Loading
|
_updateState.value = DataState.Loading
|
||||||
localSource.getFileStream(uri)?.let { fileStream ->
|
provider.getLocalFileStream(uri)?.let { fileStream ->
|
||||||
delay(updateStateDelay)
|
delay(updateStateDelay)
|
||||||
localSource.insertEntries(importSatellites(fileStream))
|
storage.insertEntries(importSatellites(fileStream))
|
||||||
}
|
}
|
||||||
_updateState.value = DataState.Success(0L)
|
_updateState.value = DataState.Success(0L)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFromWeb(sources: List<String>) {
|
override fun updateFromWeb(urls: List<String>) {
|
||||||
_updateState.value = DataState.Loading
|
_updateState.value = DataState.Loading
|
||||||
repositoryScope.launch(exceptionHandler) {
|
repoScope.launch(exceptionHandler) {
|
||||||
localSource.setDataSources(sources)
|
|
||||||
}
|
|
||||||
repositoryScope.launch(exceptionHandler) {
|
|
||||||
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
|
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
|
||||||
val streamsMap = mutableMapOf<String, InputStream?>()
|
val streamsMap = mutableMapOf<String, InputStream?>()
|
||||||
val streams = mutableListOf<InputStream>()
|
val streams = mutableListOf<InputStream>()
|
||||||
val entries = mutableListOf<SatEntry>()
|
val entries = mutableListOf<SatEntry>()
|
||||||
sources.forEach { jobsMap[it] = async { remoteSource.getFileStream(it) } }
|
urls.forEach { jobsMap[it] = async { provider.getRemoteFileStream(it) } }
|
||||||
jobsMap.forEach { job -> streamsMap[job.key] = job.value.await() }
|
jobsMap.forEach { job -> streamsMap[job.key] = job.value.await() }
|
||||||
streamsMap.forEach { stream ->
|
streamsMap.forEach { stream ->
|
||||||
stream.value?.let { inputStream ->
|
stream.value?.let { inputStream ->
|
||||||
@@ -93,29 +85,27 @@ class DataRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
streams.forEach { stream -> entries.addAll(importSatellites(stream)) }
|
||||||
localSource.insertEntries(entries)
|
storage.insertEntries(entries)
|
||||||
}
|
}
|
||||||
repositoryScope.launch(exceptionHandler) {
|
repoScope.launch(exceptionHandler) {
|
||||||
remoteSource.getFileStream(remoteSource.radioApi)?.let { stream ->
|
provider.getRemoteFileStream(provider.radioApi)?.let { stream ->
|
||||||
localSource.insertRadios(dataParser.parseJSONStream(stream))
|
storage.insertRadios(dataParser.parseJSONStream(stream))
|
||||||
_updateState.value = DataState.Success(0L)
|
_updateState.value = DataState.Success(0L)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearAllData() {
|
override fun setUpdateStateHandled() {
|
||||||
repositoryScope.launch {
|
_updateState.value = DataState.Handled
|
||||||
_updateState.value = DataState.Loading
|
|
||||||
delay(updateStateDelay)
|
|
||||||
localSource.clearAllData()
|
|
||||||
_updateState.value = DataState.Success(0L)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getDataSources() = localSource.getDataSources()
|
override fun clearAllData() {
|
||||||
|
repoScope.launch {
|
||||||
override suspend fun setEntriesSelection(catnums: List<Int>) {
|
_updateState.value = DataState.Loading
|
||||||
localSource.setEntriesSelection(catnums)
|
delay(updateStateDelay)
|
||||||
|
storage.clearAllData()
|
||||||
|
_updateState.value = DataState.Success(0L)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
|
private suspend fun importSatellites(stream: InputStream): List<SatEntry> {
|
||||||
+8
-12
@@ -24,29 +24,25 @@ import com.rtbishop.look4sat.domain.predict.Satellite
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
interface IDataRepository {
|
interface IRepository {
|
||||||
|
|
||||||
val updateState: StateFlow<DataState<Long>>
|
val updateState: StateFlow<DataState<Long>>
|
||||||
|
|
||||||
fun setUpdateStateHandled()
|
fun getEntriesTotal(): Flow<Int>
|
||||||
|
|
||||||
fun getEntriesNumber(): Flow<Int>
|
fun getRadiosTotal(): Flow<Int>
|
||||||
|
|
||||||
fun getRadiosNumber(): Flow<Int>
|
|
||||||
|
|
||||||
suspend fun getEntriesWithModes(): List<SatItem>
|
suspend fun getEntriesWithModes(): List<SatItem>
|
||||||
|
|
||||||
suspend fun getSelectedEntries(): List<Satellite>
|
suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite>
|
||||||
|
|
||||||
suspend fun getRadios(catnum: Int): List<SatRadio>
|
suspend fun getRadiosWithId(id: Int): List<SatRadio>
|
||||||
|
|
||||||
fun updateFromFile(uri: String)
|
fun updateFromFile(uri: String)
|
||||||
|
|
||||||
fun updateFromWeb(sources: List<String>)
|
fun updateFromWeb(urls: List<String>)
|
||||||
|
|
||||||
|
fun setUpdateStateHandled()
|
||||||
|
|
||||||
fun clearAllData()
|
fun clearAllData()
|
||||||
|
|
||||||
suspend fun getDataSources(): List<String>
|
|
||||||
|
|
||||||
suspend fun setEntriesSelection(catnums: List<Int>)
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -15,11 +15,11 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.data
|
package com.rtbishop.look4sat.domain
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||||
|
|
||||||
interface ISettingsHandler {
|
interface ISettings {
|
||||||
|
|
||||||
val defaultSources: List<String>
|
val defaultSources: List<String>
|
||||||
get() = listOf(
|
get() = listOf(
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat
|
package com.rtbishop.look4sat
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.DataParser
|
import com.rtbishop.look4sat.data.DataParser
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|||||||
Reference in new issue
Block a user