mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Switched to a generic LocationHandler.kt
This commit is contained in:
1 parent
eebe691419
commit
a9a4aa9737
24 files changed
+430
-525
No files matched your search
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.domain.LocationHandler
|
||||
import com.rtbishop.look4sat.domain.QthConverter
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.presentation.round
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class LocationProvider @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val settingsProvider: SettingsProvider,
|
||||
) : LocationListener, LocationHandler {
|
||||
|
||||
private val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
private val providerDef = LocationManager.PASSIVE_PROVIDER
|
||||
private val providerNet = LocationManager.NETWORK_PROVIDER
|
||||
private val providerGps = LocationManager.GPS_PROVIDER
|
||||
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
private val _stationPosition = MutableSharedFlow<DataState<GeoPos>>(
|
||||
extraBufferCapacity = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
private var currentPosition = settingsProvider.loadStationPosition()
|
||||
override val stationPosition: SharedFlow<DataState<GeoPos>> = _stationPosition
|
||||
|
||||
override fun getStationPosition(): GeoPos = currentPosition
|
||||
|
||||
override fun setStationPosition(latitude: Double, longitude: Double) {
|
||||
if (QthConverter.isValidPosition(latitude, longitude)) {
|
||||
val newLat = latitude.round(4)
|
||||
val newLon = longitude.round(4)
|
||||
currentPosition = GeoPos(newLat, newLon)
|
||||
settingsProvider.saveStationPosition(newLat, newLon)
|
||||
_stationPosition.tryEmit(DataState.Success(currentPosition))
|
||||
} else _stationPosition.tryEmit(DataState.Error(context.getString(R.string.pref_pos_gps_null)))
|
||||
}
|
||||
|
||||
override fun setPositionFromLocation() {
|
||||
val result = ContextCompat.checkSelfPermission(context, locationFine)
|
||||
if (manager.isProviderEnabled(providerDef) && result == PackageManager.PERMISSION_GRANTED) {
|
||||
val location = manager.getLastKnownLocation(providerDef)
|
||||
if (location != null) {
|
||||
setStationPosition(location.latitude, location.longitude)
|
||||
} else setPositionFromGps()
|
||||
} else _stationPosition.tryEmit(DataState.Error(context.getString(R.string.pref_pos_gps_null)))
|
||||
}
|
||||
|
||||
override fun setPositionFromNet() {
|
||||
val result = ContextCompat.checkSelfPermission(context, locationCoarse)
|
||||
if (manager.isProviderEnabled(providerNet) && result == PackageManager.PERMISSION_GRANTED) {
|
||||
_stationPosition.tryEmit(DataState.Loading)
|
||||
manager.requestLocationUpdates(providerNet, 0L, 0f, this)
|
||||
} else _stationPosition.tryEmit(DataState.Error(context.getString(R.string.pref_pos_gps_null)))
|
||||
}
|
||||
|
||||
override fun setPositionFromGps() {
|
||||
val result = ContextCompat.checkSelfPermission(context, locationFine)
|
||||
if (manager.isProviderEnabled(providerGps) && result == PackageManager.PERMISSION_GRANTED) {
|
||||
_stationPosition.tryEmit(DataState.Loading)
|
||||
manager.requestLocationUpdates(providerGps, 0L, 0f, this)
|
||||
} else _stationPosition.tryEmit(DataState.Error(context.getString(R.string.pref_pos_gps_null)))
|
||||
}
|
||||
|
||||
override fun setPositionFromQth(qthString: String) {
|
||||
val position = QthConverter.qthToPosition(qthString)
|
||||
if (position != null) {
|
||||
setStationPosition(position.latitude, position.longitude)
|
||||
} else _stationPosition.tryEmit(DataState.Error(context.getString(R.string.pref_pos_qth_error)))
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
manager.removeUpdates(this)
|
||||
setStationPosition(location.latitude, location.longitude)
|
||||
}
|
||||
}
|
||||
@@ -1,92 +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 android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.GeomagneticField
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.rtbishop.look4sat.domain.LocationProvider
|
||||
import com.rtbishop.look4sat.domain.QthConverter
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.presentation.round
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class LocationSource @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val preferences: SharedPreferences
|
||||
) : LocationListener, LocationProvider {
|
||||
|
||||
private val locManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
private val _updatedLocation = MutableSharedFlow<GeoPos?>()
|
||||
override val updatedLocation: SharedFlow<GeoPos?> = _updatedLocation
|
||||
|
||||
fun getMagDeclination(stationPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
||||
val lat = stationPos.latitude.toFloat()
|
||||
val lon = stationPos.longitude.toFloat()
|
||||
return GeomagneticField(lat, lon, 0f, time).declination
|
||||
}
|
||||
|
||||
fun updatePosition(latitude: Double, longitude: Double) {
|
||||
if (QthConverter.isValidPosition(latitude, longitude)) {
|
||||
_updatedLocation.tryEmit(GeoPos(latitude, longitude))
|
||||
} else _updatedLocation.tryEmit(null)
|
||||
}
|
||||
|
||||
fun updatePositionFromQth(qthString: String) {
|
||||
val position = QthConverter.qthToPosition(qthString)
|
||||
if (position != null) {
|
||||
_updatedLocation.tryEmit(GeoPos(position.latitude, position.longitude))
|
||||
} else _updatedLocation.tryEmit(null)
|
||||
}
|
||||
|
||||
fun updatePositionFromGps() {
|
||||
val provider = LocationManager.GPS_PROVIDER
|
||||
val permission = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
val result = ContextCompat.checkSelfPermission(context, permission)
|
||||
if (locManager.isProviderEnabled(provider) && result == PackageManager.PERMISSION_GRANTED) {
|
||||
locManager.requestLocationUpdates(provider, 0L, 0f, this)
|
||||
} else _updatedLocation.tryEmit(null)
|
||||
}
|
||||
|
||||
fun updatePositionFromNetwork() {
|
||||
val provider = LocationManager.NETWORK_PROVIDER
|
||||
val permission = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
val result = ContextCompat.checkSelfPermission(context, permission)
|
||||
if (locManager.isProviderEnabled(provider) && result == PackageManager.PERMISSION_GRANTED) {
|
||||
locManager.requestLocationUpdates(provider, 0L, 0f, this)
|
||||
} else _updatedLocation.tryEmit(null)
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
locManager.removeUpdates(this)
|
||||
val latitude = location.latitude.round(4)
|
||||
val longitude = location.longitude.round(4)
|
||||
_updatedLocation.tryEmit(GeoPos(latitude, longitude))
|
||||
}
|
||||
}
|
||||
@@ -1,198 +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 android.content.SharedPreferences
|
||||
import android.hardware.GeomagneticField
|
||||
import android.location.LocationManager
|
||||
import androidx.core.content.edit
|
||||
import com.rtbishop.look4sat.BuildConfig
|
||||
import com.rtbishop.look4sat.data.PreferencesHandler
|
||||
import com.rtbishop.look4sat.domain.QthConverter
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.presentation.round
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class PreferencesSource @Inject constructor(
|
||||
private val locationManager: LocationManager,
|
||||
private val preferences: SharedPreferences
|
||||
) : PreferencesHandler {
|
||||
|
||||
private val keyInitialSetup = "${BuildConfig.VERSION_NAME}update"
|
||||
|
||||
companion object {
|
||||
const val keyDataSources = "dataSources"
|
||||
const val keyModes = "satModes"
|
||||
const val keyCompass = "compass"
|
||||
const val keyRadarSweep = "radarSweep"
|
||||
const val keyTimeUTC = "timeUTC"
|
||||
const val keyHoursAhead = "hoursAhead"
|
||||
const val keyMinElevation = "minElevation"
|
||||
const val keyRotator = "isRotatorEnabled"
|
||||
const val keyRotatorAddress = "rotatorAddress"
|
||||
const val keyRotatorPort = "rotatorPort"
|
||||
const val keyLatitude = "stationLat"
|
||||
const val keyLongitude = "stationLon"
|
||||
const val keyPositionGPS = "setPositionGPS"
|
||||
const val keyPositionQTH = "setPositionQTH"
|
||||
}
|
||||
|
||||
fun loadStationPosition(): GeoPos {
|
||||
val defaultSP = "0.0"
|
||||
val latitude = preferences.getString(keyLatitude, null) ?: defaultSP
|
||||
val longitude = preferences.getString(keyLongitude, null) ?: defaultSP
|
||||
return GeoPos(latitude.toDouble(), longitude.toDouble())
|
||||
}
|
||||
|
||||
private fun saveStationPosition(pos: GeoPos) {
|
||||
preferences.edit {
|
||||
putString(keyLatitude, pos.latitude.toString())
|
||||
putString(keyLongitude, pos.longitude.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePosition(latitude: Double, longitude: Double) {
|
||||
val stationPosition = GeoPos(latitude, longitude)
|
||||
saveStationPosition(stationPosition)
|
||||
}
|
||||
|
||||
fun updatePositionFromGPS(): Boolean {
|
||||
return try {
|
||||
val location = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
|
||||
if (location == null) false
|
||||
else {
|
||||
val latitude = location.latitude.round(4)
|
||||
val longitude = location.longitude.round(4)
|
||||
val stationPosition = GeoPos(latitude, longitude)
|
||||
saveStationPosition(stationPosition)
|
||||
return true
|
||||
}
|
||||
} catch (exception: SecurityException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePositionFromQTH(qthString: String): Boolean {
|
||||
val position = QthConverter.qthToPosition(qthString) ?: return false
|
||||
val stationPosition = GeoPos(position.latitude, position.longitude)
|
||||
saveStationPosition(stationPosition)
|
||||
return true
|
||||
}
|
||||
|
||||
fun getMagDeclination(): Float {
|
||||
val stationPosition = loadStationPosition()
|
||||
val lat = stationPosition.latitude.toFloat()
|
||||
val lon = stationPosition.longitude.toFloat()
|
||||
return GeomagneticField(lat, lon, 0f, System.currentTimeMillis()).declination
|
||||
}
|
||||
|
||||
fun getHoursAhead(): Int {
|
||||
return preferences.getInt(keyHoursAhead, 8)
|
||||
}
|
||||
|
||||
fun getMinElevation(): Double {
|
||||
return preferences.getInt(keyMinElevation, 16).toDouble()
|
||||
}
|
||||
|
||||
fun getUseUTC(): Boolean {
|
||||
return preferences.getBoolean(keyTimeUTC, false)
|
||||
}
|
||||
|
||||
fun setUseUTC(value: Boolean) {
|
||||
preferences.edit { putBoolean(keyTimeUTC, value) }
|
||||
}
|
||||
|
||||
fun getUseCompass(): Boolean {
|
||||
return preferences.getBoolean(keyCompass, true)
|
||||
}
|
||||
|
||||
fun setUseCompass(value: Boolean) {
|
||||
preferences.edit { putBoolean(keyCompass, value) }
|
||||
}
|
||||
|
||||
fun getShowSweep(): Boolean {
|
||||
return preferences.getBoolean(keyRadarSweep, true)
|
||||
}
|
||||
|
||||
fun setShowSweep(value: Boolean) {
|
||||
preferences.edit { putBoolean(keyRadarSweep, value) }
|
||||
}
|
||||
|
||||
fun getSetupDone(): Boolean {
|
||||
return preferences.getBoolean(keyInitialSetup, false)
|
||||
}
|
||||
|
||||
fun setSetupDone() {
|
||||
preferences.edit { putBoolean(keyInitialSetup, true) }
|
||||
}
|
||||
|
||||
fun saveModesSelection(modes: List<String>) {
|
||||
val modesSet = modes.toSet()
|
||||
preferences.edit {
|
||||
putStringSet(keyModes, modesSet)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadModesSelection(): List<String> {
|
||||
preferences.getStringSet(keyModes, setOf())?.let { modesSet ->
|
||||
return modesSet.toList().sorted()
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
fun getRotatorEnabled(): Boolean {
|
||||
return preferences.getBoolean(keyRotator, false)
|
||||
}
|
||||
|
||||
fun setRotatorEnabled(value: Boolean) {
|
||||
preferences.edit { putBoolean(keyRotator, value) }
|
||||
}
|
||||
|
||||
fun getRotatorServer(): Pair<String, Int> {
|
||||
val address = preferences.getString(keyRotatorAddress, null) ?: "127.0.0.1"
|
||||
val port = preferences.getString(keyRotatorPort, null) ?: "4533"
|
||||
return Pair(address, port.toInt())
|
||||
}
|
||||
|
||||
fun getRotatorIp(): String {
|
||||
return preferences.getString(keyRotatorAddress, null) ?: "127.0.0.1"
|
||||
}
|
||||
|
||||
fun setRotatorIp(value: String) {
|
||||
preferences.edit { putString(keyRotatorAddress, value) }
|
||||
}
|
||||
|
||||
fun getRotatorPort(): String {
|
||||
return preferences.getString(keyRotatorPort, null) ?: "4533"
|
||||
}
|
||||
|
||||
fun setRotatorPort(value: String) {
|
||||
preferences.edit { putString(keyRotatorPort, value) }
|
||||
}
|
||||
|
||||
override fun loadDataSources(): List<String> {
|
||||
val sourcesList = preferences.getStringSet(keyDataSources, null)?.toList()
|
||||
return if (sourcesList.isNullOrEmpty()) defaultSources else sourcesList.sortedDescending()
|
||||
}
|
||||
|
||||
override fun saveDataSources(sources: List<String>) {
|
||||
if (sources.isNotEmpty()) preferences.edit { putStringSet(keyDataSources, sources.toSet()) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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 android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.rtbishop.look4sat.BuildConfig
|
||||
import com.rtbishop.look4sat.data.SettingsHandler
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class SettingsProvider @Inject constructor(private val prefs: SharedPreferences) : SettingsHandler {
|
||||
|
||||
private val keyInitialSetup = "${BuildConfig.VERSION_NAME}update"
|
||||
|
||||
companion object {
|
||||
const val keyDataSources = "dataSources"
|
||||
const val keyModes = "satModes"
|
||||
const val keyCompass = "compass"
|
||||
const val keyRadarSweep = "radarSweep"
|
||||
const val keyTimeUTC = "timeUTC"
|
||||
const val keyHoursAhead = "hoursAhead"
|
||||
const val keyMinElevation = "minElevation"
|
||||
const val keyRotator = "isRotatorEnabled"
|
||||
const val keyRotatorAddress = "rotatorAddress"
|
||||
const val keyRotatorPort = "rotatorPort"
|
||||
const val keyLatitude = "stationLat"
|
||||
const val keyLongitude = "stationLon"
|
||||
const val keyPositionGPS = "setPositionGPS"
|
||||
const val keyPositionQTH = "setPositionQTH"
|
||||
}
|
||||
|
||||
fun loadStationPosition(): GeoPos {
|
||||
val defaultSP = "0.0"
|
||||
val latitude = prefs.getString(keyLatitude, null) ?: defaultSP
|
||||
val longitude = prefs.getString(keyLongitude, null) ?: defaultSP
|
||||
return GeoPos(latitude.toDouble(), longitude.toDouble())
|
||||
}
|
||||
|
||||
fun saveStationPosition(latitude: Double, longitude: Double) {
|
||||
prefs.edit {
|
||||
putString(keyLatitude, latitude.toString())
|
||||
putString(keyLongitude, longitude.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun getHoursAhead(): Int {
|
||||
return prefs.getInt(keyHoursAhead, 8)
|
||||
}
|
||||
|
||||
fun getMinElevation(): Double {
|
||||
return prefs.getInt(keyMinElevation, 16).toDouble()
|
||||
}
|
||||
|
||||
fun getUseUTC(): Boolean {
|
||||
return prefs.getBoolean(keyTimeUTC, false)
|
||||
}
|
||||
|
||||
fun setUseUTC(value: Boolean) {
|
||||
prefs.edit { putBoolean(keyTimeUTC, value) }
|
||||
}
|
||||
|
||||
fun getUseCompass(): Boolean {
|
||||
return prefs.getBoolean(keyCompass, true)
|
||||
}
|
||||
|
||||
fun setUseCompass(value: Boolean) {
|
||||
prefs.edit { putBoolean(keyCompass, value) }
|
||||
}
|
||||
|
||||
fun getShowSweep(): Boolean {
|
||||
return prefs.getBoolean(keyRadarSweep, true)
|
||||
}
|
||||
|
||||
fun setShowSweep(value: Boolean) {
|
||||
prefs.edit { putBoolean(keyRadarSweep, value) }
|
||||
}
|
||||
|
||||
fun getSetupDone(): Boolean {
|
||||
return prefs.getBoolean(keyInitialSetup, false)
|
||||
}
|
||||
|
||||
fun setSetupDone() {
|
||||
prefs.edit { putBoolean(keyInitialSetup, true) }
|
||||
}
|
||||
|
||||
fun saveModesSelection(modes: List<String>) {
|
||||
val modesSet = modes.toSet()
|
||||
prefs.edit {
|
||||
putStringSet(keyModes, modesSet)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadModesSelection(): List<String> {
|
||||
prefs.getStringSet(keyModes, setOf())?.let { modesSet ->
|
||||
return modesSet.toList().sorted()
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
fun getRotatorEnabled(): Boolean {
|
||||
return prefs.getBoolean(keyRotator, false)
|
||||
}
|
||||
|
||||
fun setRotatorEnabled(value: Boolean) {
|
||||
prefs.edit { putBoolean(keyRotator, value) }
|
||||
}
|
||||
|
||||
fun getRotatorServer(): Pair<String, Int> {
|
||||
val address = prefs.getString(keyRotatorAddress, null) ?: "127.0.0.1"
|
||||
val port = prefs.getString(keyRotatorPort, null) ?: "4533"
|
||||
return Pair(address, port.toInt())
|
||||
}
|
||||
|
||||
fun getRotatorIp(): String {
|
||||
return prefs.getString(keyRotatorAddress, null) ?: "127.0.0.1"
|
||||
}
|
||||
|
||||
fun setRotatorIp(value: String) {
|
||||
prefs.edit { putString(keyRotatorAddress, value) }
|
||||
}
|
||||
|
||||
fun getRotatorPort(): String {
|
||||
return prefs.getString(keyRotatorPort, null) ?: "4533"
|
||||
}
|
||||
|
||||
fun setRotatorPort(value: String) {
|
||||
prefs.edit { putString(keyRotatorPort, value) }
|
||||
}
|
||||
|
||||
override fun loadDataSources(): List<String> {
|
||||
val sourcesList = prefs.getStringSet(keyDataSources, null)?.toList()
|
||||
return if (sourcesList.isNullOrEmpty()) defaultSources else sourcesList.sortedDescending()
|
||||
}
|
||||
|
||||
override fun saveDataSources(sources: List<String>) {
|
||||
if (sources.isNotEmpty()) prefs.edit { putStringSet(keyDataSources, sources.toSet()) }
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,9 @@ interface EntriesDao {
|
||||
suspend fun insertEntries(entries: List<SatEntry>)
|
||||
|
||||
@Transaction
|
||||
suspend fun updateEntries(entries: List<SatEntry>) {
|
||||
suspend fun updateEntries(entries: List<SatEntry>, cleanup: Boolean = false) {
|
||||
val entriesSelection = getEntriesSelection()
|
||||
deleteEntries()
|
||||
if (cleanup) deleteEntries()
|
||||
insertEntries(entries)
|
||||
restoreSelection(entriesSelection, true)
|
||||
}
|
||||
|
||||
@@ -19,15 +19,16 @@ package com.rtbishop.look4sat.injection
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.location.LocationManager
|
||||
import androidx.room.Room
|
||||
import com.rtbishop.look4sat.data.DefaultRepository
|
||||
import com.rtbishop.look4sat.data.PreferencesHandler
|
||||
import com.rtbishop.look4sat.data.SettingsHandler
|
||||
import com.rtbishop.look4sat.domain.DataParser
|
||||
import com.rtbishop.look4sat.domain.DataReporter
|
||||
import com.rtbishop.look4sat.domain.DataRepository
|
||||
import com.rtbishop.look4sat.domain.LocationHandler
|
||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.framework.LocationProvider
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import com.rtbishop.look4sat.framework.local.LocalSource
|
||||
import com.rtbishop.look4sat.framework.local.SatelliteDb
|
||||
import com.rtbishop.look4sat.framework.remote.RemoteSource
|
||||
@@ -46,7 +47,7 @@ object CoreModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSatelliteRepo(
|
||||
preferencesHandler: PreferencesHandler,
|
||||
settingsHandler: SettingsHandler,
|
||||
@ApplicationContext context: Context,
|
||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
|
||||
@@ -56,16 +57,22 @@ object CoreModule {
|
||||
.fallbackToDestructiveMigration().build()
|
||||
val localSource = LocalSource(db.entriesDao(), db.transmittersDao())
|
||||
val remoteSource = RemoteSource(ioDispatcher)
|
||||
return DefaultRepository(dataParser, localSource, remoteSource, preferencesHandler)
|
||||
return DefaultRepository(dataParser, localSource, remoteSource, settingsHandler)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePreferencesHandler(
|
||||
locationManager: LocationManager,
|
||||
sharedPreferences: SharedPreferences
|
||||
): PreferencesHandler {
|
||||
return PreferencesSource(locationManager, sharedPreferences)
|
||||
fun provideSettingsHandler(sharedPreferences: SharedPreferences): SettingsHandler {
|
||||
return SettingsProvider(sharedPreferences)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLocationHandler(
|
||||
@ApplicationContext context: Context,
|
||||
settingsProvider: SettingsProvider
|
||||
): LocationHandler {
|
||||
return LocationProvider(context, settingsProvider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -89,8 +89,6 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
||||
val message = getString(R.string.entries_update_error)
|
||||
Snackbar.make(requireView(), message, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -24,7 +24,7 @@ import androidx.lifecycle.*
|
||||
import com.rtbishop.look4sat.domain.DataRepository
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -34,14 +34,14 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class EntriesViewModel @Inject constructor(
|
||||
private val preferences: PreferencesSource,
|
||||
private val preferences: SettingsProvider,
|
||||
private val resolver: ContentResolver,
|
||||
private val dataRepository: DataRepository
|
||||
) : ViewModel(), SearchView.OnQueryTextListener, EntriesAdapter.EntriesClickListener {
|
||||
|
||||
private val coroutineHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
Timber.d(throwable)
|
||||
_satData.value = DataState.Error(throwable)
|
||||
_satData.value = DataState.Error(null)
|
||||
}
|
||||
private val transModes = MutableLiveData(preferences.loadModesSelection())
|
||||
private val currentQuery = MutableLiveData(String())
|
||||
@@ -64,7 +64,7 @@ class EntriesViewModel @Inject constructor(
|
||||
resolver.openInputStream(uri)?.use { stream ->
|
||||
dataRepository.updateDataFromFile(stream)
|
||||
}
|
||||
}.onFailure { _satData.value = DataState.Error(it) }
|
||||
}.onFailure { _satData.value = DataState.Error(null) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.rtbishop.look4sat.domain.QthConverter
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
@@ -35,7 +35,7 @@ import kotlin.math.min
|
||||
class MapViewModel @Inject constructor(
|
||||
private val dataRepository: DataRepository,
|
||||
private val predictor: Predictor,
|
||||
preferences: PreferencesSource,
|
||||
preferences: SettingsProvider,
|
||||
) : ViewModel() {
|
||||
|
||||
private val stationPos = preferences.loadStationPosition()
|
||||
|
||||
+10
-12
@@ -17,10 +17,8 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.passesScreen
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -43,15 +41,15 @@ import java.util.*
|
||||
class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener {
|
||||
|
||||
private val passesViewModel: PassesViewModel by viewModels()
|
||||
private val permReqContract = ActivityResultContracts.RequestMultiplePermissions()
|
||||
private val locPermFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
private val locPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
private val locPermReq = registerForActivityResult(permReqContract) { permissions ->
|
||||
when {
|
||||
permissions[locPermFine] == true -> passesViewModel.triggerInitialSetup()
|
||||
permissions[locPermCoarse] == true -> passesViewModel.triggerInitialSetup()
|
||||
}
|
||||
}
|
||||
// private val permReqContract = ActivityResultContracts.RequestMultiplePermissions()
|
||||
// private val locPermFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
// private val locPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
// private val locPermReq = registerForActivityResult(permReqContract) { permissions ->
|
||||
// when {
|
||||
// permissions[locPermFine] == true -> passesViewModel.triggerInitialSetup()
|
||||
// permissions[locPermCoarse] == true -> passesViewModel.triggerInitialSetup()
|
||||
// }
|
||||
// }
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@@ -88,7 +86,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
||||
handleNewPasses(passesResult, passesAdapter, binding)
|
||||
})
|
||||
passesViewModel.isFirstLaunchDone.observe(viewLifecycleOwner, { setupDone ->
|
||||
if (!setupDone) locPermReq.launch(arrayOf(locPermFine, locPermCoarse))
|
||||
// if (!setupDone) locPermReq.launch(arrayOf(locPermFine, locPermCoarse))
|
||||
})
|
||||
getNavResult<Pair<Int, Double>>(R.id.nav_passes, "prefs") { prefs ->
|
||||
passesViewModel.forceCalculation(prefs.first, prefs.second)
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.DialogPassesBinding
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import com.rtbishop.look4sat.presentation.setNavResult
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
@@ -17,7 +17,7 @@ import javax.inject.Inject
|
||||
class PassesPrefsDialog : AppCompatDialogFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var preferences: PreferencesSource
|
||||
lateinit var preferences: SettingsProvider
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_passes, group, false)
|
||||
|
||||
+4
-5
@@ -25,7 +25,7 @@ import com.rtbishop.look4sat.domain.DataRepository
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.*
|
||||
import javax.inject.Inject
|
||||
@@ -34,7 +34,7 @@ import javax.inject.Inject
|
||||
class PassesViewModel @Inject constructor(
|
||||
private val dataRepository: DataRepository,
|
||||
private val predictor: Predictor,
|
||||
private val preferences: PreferencesSource
|
||||
private val preferences: SettingsProvider
|
||||
) : ViewModel() {
|
||||
|
||||
private val _passes = MutableLiveData<DataState<List<SatPass>>>()
|
||||
@@ -66,7 +66,7 @@ class PassesViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun triggerInitialSetup() {
|
||||
preferences.updatePositionFromGPS()
|
||||
// preferences.updatePositionFromGPS()
|
||||
viewModelScope.launch {
|
||||
// _passes.postValue(DataState.Loading)
|
||||
// val satellites = dataRepository.getSelectedSatellites()
|
||||
@@ -115,8 +115,7 @@ class PassesViewModel @Inject constructor(
|
||||
}
|
||||
currentPasses = currentPasses.filter { it.progress < 100 }
|
||||
val passesCopy = currentPasses.map { it.copy() }
|
||||
if (passesCopy.isEmpty()) _passes.postValue(DataState.Empty)
|
||||
else _passes.postValue(DataState.Success(passesCopy))
|
||||
_passes.postValue(DataState.Success(passesCopy))
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentRadarBinding
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import com.rtbishop.look4sat.presentation.ItemDivider
|
||||
import com.rtbishop.look4sat.presentation.navigateSafe
|
||||
import com.rtbishop.look4sat.presentation.toTimerString
|
||||
@@ -40,7 +40,7 @@ import javax.inject.Inject
|
||||
class RadarFragment : Fragment(R.layout.fragment_radar) {
|
||||
|
||||
@Inject
|
||||
lateinit var preferences: PreferencesSource
|
||||
lateinit var preferences: SettingsProvider
|
||||
private val viewModel: RadarViewModel by viewModels()
|
||||
private var radarView: RadarView? = null
|
||||
|
||||
|
||||
@@ -17,15 +17,17 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.presentation.radarScreen
|
||||
|
||||
import android.hardware.GeomagneticField
|
||||
import androidx.lifecycle.*
|
||||
import com.rtbishop.look4sat.domain.DataReporter
|
||||
import com.rtbishop.look4sat.domain.DataRepository
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||
import com.rtbishop.look4sat.domain.predict.SatPass
|
||||
import com.rtbishop.look4sat.domain.predict.SatPos
|
||||
import com.rtbishop.look4sat.domain.model.Transmitter
|
||||
import com.rtbishop.look4sat.framework.OrientationSource
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import com.rtbishop.look4sat.presentation.round
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -37,7 +39,7 @@ import javax.inject.Inject
|
||||
@HiltViewModel
|
||||
class RadarViewModel @Inject constructor(
|
||||
private val orientationSource: OrientationSource,
|
||||
private val preferences: PreferencesSource,
|
||||
private val preferences: SettingsProvider,
|
||||
private val predictor: Predictor,
|
||||
private val dataRepository: DataRepository,
|
||||
private val dataReporter: DataReporter
|
||||
@@ -71,7 +73,13 @@ class RadarViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
|
||||
_orientation.value = Triple(azimuth + preferences.getMagDeclination(), pitch, roll)
|
||||
_orientation.value = Triple(azimuth + getMagDeclination(stationPos), pitch, roll)
|
||||
}
|
||||
|
||||
private fun getMagDeclination(geoPos: GeoPos, time: Long = System.currentTimeMillis()): Float {
|
||||
val latitude = geoPos.latitude.toFloat()
|
||||
val longitude = geoPos.longitude.toFloat()
|
||||
return GeomagneticField(latitude, longitude, 0f, time).declination
|
||||
}
|
||||
|
||||
private fun sendPassData(satPass: SatPass) {
|
||||
|
||||
+80
-107
@@ -18,12 +18,8 @@
|
||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.ContentResolver
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
@@ -31,170 +27,161 @@ import android.view.View
|
||||
import android.widget.EditText
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.rtbishop.look4sat.BuildConfig
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentSettingsBinding
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import com.rtbishop.look4sat.domain.DataRepository
|
||||
import com.rtbishop.look4sat.domain.LocationHandler
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||
import com.rtbishop.look4sat.presentation.*
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SettingsFragment : Fragment(R.layout.fragment_settings), LocationListener {
|
||||
class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
|
||||
@Inject
|
||||
lateinit var preferences: PreferencesSource
|
||||
lateinit var preferences: SettingsProvider
|
||||
|
||||
private val viewModel: SettingsViewModel by viewModels()
|
||||
private val locPermFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
private val locPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
private val contentContract = ActivityResultContracts.GetContent()
|
||||
private val filePicker = registerForActivityResult(contentContract) { uri ->
|
||||
uri?.let { viewModel.updateDataFromFile(uri) }
|
||||
}
|
||||
private val permReqContract = ActivityResultContracts.RequestMultiplePermissions()
|
||||
private val locPermReq = registerForActivityResult(permReqContract) { permissions ->
|
||||
@Inject
|
||||
lateinit var locationHandler: LocationHandler
|
||||
|
||||
@Inject
|
||||
lateinit var resolver: ContentResolver
|
||||
|
||||
@Inject
|
||||
lateinit var dataRepository: DataRepository
|
||||
|
||||
private val locationFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||
private val locationCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
private val locationContract = ActivityResultContracts.RequestMultiplePermissions()
|
||||
private val locationRequest = registerForActivityResult(locationContract) { permissions ->
|
||||
when {
|
||||
permissions[locPermFine] == true -> updatePositionFromGPS()
|
||||
permissions[locPermCoarse] == true -> updatePositionFromGPS()
|
||||
permissions[locationFine] == true -> locationHandler.setPositionFromLocation()
|
||||
permissions[locationCoarse] == true -> locationHandler.setPositionFromNet()
|
||||
else -> showSnack(getString(R.string.pref_pos_gps_error))
|
||||
}
|
||||
}
|
||||
private var _binding: FragmentSettingsBinding? = null
|
||||
private val contentContract = ActivityResultContracts.GetContent()
|
||||
private val contentRequest = registerForActivityResult(contentContract) { uri ->
|
||||
lifecycleScope.launchWhenResumed {
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
resolver.openInputStream(uri)?.use { dataRepository.updateDataFromFile(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = FragmentSettingsBinding.bind(view)
|
||||
_binding?.prefsBack?.setOnClickListener { findNavController().navigateUp() }
|
||||
setupAboutCard()
|
||||
setupDataCard()
|
||||
setupLocationCard()
|
||||
setupTrackingCard()
|
||||
setupOtherCard()
|
||||
setupWarrantyCard()
|
||||
val settingsBinding = FragmentSettingsBinding.bind(view)
|
||||
settingsBinding.prefsBack.setOnClickListener { findNavController().navigateUp() }
|
||||
setupAboutCard(settingsBinding)
|
||||
setupDataCard(settingsBinding)
|
||||
setupLocationCard(settingsBinding)
|
||||
setupTrackingCard(settingsBinding)
|
||||
setupOtherCard(settingsBinding)
|
||||
setupWarrantyCard(settingsBinding)
|
||||
locationHandler.stationPosition.asLiveData().observe(viewLifecycleOwner) { stationPos ->
|
||||
stationPos?.let { handleStationPosition(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAboutCard() {
|
||||
_binding?.prefsInfo?.let { binding ->
|
||||
binding.aboutVersion.text =
|
||||
private fun setupAboutCard(binding: FragmentSettingsBinding) {
|
||||
binding.prefsInfo.aboutVersion.text =
|
||||
String.format(getString(R.string.about_version), BuildConfig.VERSION_NAME)
|
||||
binding.aboutBtnGithub.setOnClickListener {
|
||||
binding.prefsInfo.aboutBtnGithub.setOnClickListener {
|
||||
gotoUrl("https://github.com/rt-bishop/Look4Sat/")
|
||||
}
|
||||
binding.aboutBtnDonate.setOnClickListener {
|
||||
binding.prefsInfo.aboutBtnDonate.setOnClickListener {
|
||||
gotoUrl("https://www.buymeacoffee.com/rtbishop")
|
||||
}
|
||||
binding.aboutBtnFdroid.setOnClickListener {
|
||||
binding.prefsInfo.aboutBtnFdroid.setOnClickListener {
|
||||
gotoUrl("https://f-droid.org/en/packages/com.rtbishop.look4sat/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupDataCard() {
|
||||
_binding?.prefsData?.let { binding ->
|
||||
binding.updateBtnWeb.setOnClickListener {
|
||||
private fun setupDataCard(binding: FragmentSettingsBinding) {
|
||||
binding.prefsData.updateBtnFile.setOnClickListener { contentRequest.launch("*/*") }
|
||||
binding.prefsData.updateBtnWeb.setOnClickListener {
|
||||
findNavController().navigateSafe(R.id.action_prefs_to_sources)
|
||||
}
|
||||
binding.updateBtnFile.setOnClickListener { filePicker.launch("*/*") }
|
||||
getNavResult<List<String>>(R.id.nav_prefs, "sources") { sources ->
|
||||
viewModel.updateDataFromWeb(sources)
|
||||
}
|
||||
lifecycleScope.launchWhenResumed { dataRepository.updateDataFromWeb(sources) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupLocationCard() {
|
||||
_binding?.prefsLocation?.let { binding ->
|
||||
binding.locationBtnGps.setOnClickListener {
|
||||
updatePositionFromGPS()
|
||||
private fun setupLocationCard(binding: FragmentSettingsBinding) {
|
||||
binding.prefsLocation.locationBtnGps.setOnClickListener {
|
||||
locationRequest.launch(arrayOf(locationFine, locationCoarse))
|
||||
}
|
||||
binding.locationBtnQth.setOnClickListener {
|
||||
binding.prefsLocation.locationBtnQth.setOnClickListener {
|
||||
val editText = EditText(requireActivity())
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle("Title")
|
||||
.setEditText(editText)
|
||||
.setPositiveButton("OK") { _, _ ->
|
||||
val editTextInput = editText.text.toString()
|
||||
updatePositionFromQth(editTextInput)
|
||||
locationHandler.setPositionFromQth(editTextInput)
|
||||
}
|
||||
.setNeutralButton("Cancel", null)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTrackingCard() {
|
||||
_binding?.prefsTracking?.let { binding ->
|
||||
binding.trackingSwitch.apply {
|
||||
private fun setupTrackingCard(binding: FragmentSettingsBinding) {
|
||||
binding.prefsTracking.trackingSwitch.apply {
|
||||
isChecked = preferences.getRotatorEnabled()
|
||||
binding.trackingIp.isEnabled = isChecked
|
||||
binding.trackingIpEdit.setText(preferences.getRotatorIp())
|
||||
binding.trackingPort.isEnabled = isChecked
|
||||
binding.trackingPortEdit.setText(preferences.getRotatorPort())
|
||||
binding.prefsTracking.trackingIp.isEnabled = isChecked
|
||||
binding.prefsTracking.trackingIpEdit.setText(preferences.getRotatorIp())
|
||||
binding.prefsTracking.trackingPort.isEnabled = isChecked
|
||||
binding.prefsTracking.trackingPortEdit.setText(preferences.getRotatorPort())
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
preferences.setRotatorEnabled(isChecked)
|
||||
binding.trackingIp.isEnabled = isChecked
|
||||
binding.trackingPort.isEnabled = isChecked
|
||||
binding.prefsTracking.trackingIp.isEnabled = isChecked
|
||||
binding.prefsTracking.trackingPort.isEnabled = isChecked
|
||||
}
|
||||
}
|
||||
binding.trackingIpEdit.doOnTextChanged { text, _, _, _ ->
|
||||
binding.prefsTracking.trackingIpEdit.doOnTextChanged { text, _, _, _ ->
|
||||
if (text.toString().isValidIPv4()) preferences.setRotatorIp(text.toString())
|
||||
}
|
||||
binding.trackingPortEdit.doOnTextChanged { text, _, _, _ ->
|
||||
binding.prefsTracking.trackingPortEdit.doOnTextChanged { text, _, _, _ ->
|
||||
if (text.toString().isValidPort()) preferences.setRotatorPort(text.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupOtherCard() {
|
||||
_binding?.prefsOther?.let { binding ->
|
||||
binding.otherSwitchUtc.apply {
|
||||
private fun setupOtherCard(binding: FragmentSettingsBinding) {
|
||||
binding.prefsOther.otherSwitchUtc.apply {
|
||||
isChecked = preferences.getUseUTC()
|
||||
setOnCheckedChangeListener { _, isChecked -> preferences.setUseUTC(isChecked) }
|
||||
}
|
||||
binding.otherSwitchSweep.apply {
|
||||
binding.prefsOther.otherSwitchSweep.apply {
|
||||
isChecked = preferences.getShowSweep()
|
||||
setOnCheckedChangeListener { _, isChecked -> preferences.setShowSweep(isChecked) }
|
||||
}
|
||||
binding.otherSwitchSensors.apply {
|
||||
binding.prefsOther.otherSwitchSensors.apply {
|
||||
isChecked = preferences.getUseCompass()
|
||||
setOnCheckedChangeListener { _, isChecked -> preferences.setUseCompass(isChecked) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupWarrantyCard(binding: FragmentSettingsBinding) {
|
||||
binding.prefsWarranty.warrantyThanks.movementMethod = LinkMovementMethod.getInstance()
|
||||
binding.prefsWarranty.warrantyLicense.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
private fun setupWarrantyCard() {
|
||||
_binding?.prefsWarranty?.let { binding ->
|
||||
binding.warrantyThanks.movementMethod = LinkMovementMethod.getInstance()
|
||||
binding.warrantyLicense.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePositionFromGPS() {
|
||||
val manager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
val provider = LocationManager.NETWORK_PROVIDER
|
||||
val permission = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
val result = ContextCompat.checkSelfPermission(requireContext(), permission)
|
||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
||||
if (manager.isProviderEnabled(provider)) {
|
||||
manager.requestLocationUpdates(provider, 0L, 0f, this)
|
||||
} else {
|
||||
showSnack(getString(R.string.pref_pos_gps_null))
|
||||
}
|
||||
} else locPermReq.launch(arrayOf(locPermFine, locPermCoarse))
|
||||
}
|
||||
|
||||
private fun updatePositionFromQth(qthString: String) {
|
||||
if (preferences.updatePositionFromQTH(qthString)) {
|
||||
showSnack(getString(R.string.pref_pos_success))
|
||||
} else {
|
||||
showSnack(getString(R.string.pref_pos_qth_error))
|
||||
private fun handleStationPosition(position: DataState<GeoPos>) {
|
||||
when (position) {
|
||||
is DataState.Success -> showSnack(getString(R.string.pref_pos_success))
|
||||
is DataState.Error -> showSnack(position.message.toString())
|
||||
DataState.Loading -> showSnack("Fetching position")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,18 +192,4 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), LocationListener
|
||||
private fun gotoUrl(url: String) {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
_binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
val manager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
manager.removeUpdates(this)
|
||||
val latitude = location.latitude.round(4)
|
||||
val longitude = location.longitude.round(4)
|
||||
preferences.updatePosition(latitude, longitude)
|
||||
showSnack(getString(R.string.pref_pos_success))
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +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.settingsScreen
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.rtbishop.look4sat.domain.DataRepository
|
||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsViewModel @Inject constructor(
|
||||
private val preferences: PreferencesSource,
|
||||
private val resolver: ContentResolver,
|
||||
private val dataRepository: DataRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val coroutineHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
// Timber.d(throwable)
|
||||
// _satData.value = DataState.Error(throwable)
|
||||
}
|
||||
|
||||
fun updateDataFromFile(uri: Uri) {
|
||||
viewModelScope.launch(coroutineHandler) {
|
||||
// _satData.value = DataState.Loading
|
||||
runCatching {
|
||||
resolver.openInputStream(uri)?.use { stream ->
|
||||
dataRepository.updateDataFromFile(stream)
|
||||
}
|
||||
}.onFailure {
|
||||
// _satData.value = DataState.Error(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateDataFromWeb(sources: List<String>) {
|
||||
viewModelScope.launch(coroutineHandler) {
|
||||
// _satData.value = DataState.Loading
|
||||
dataRepository.updateDataFromWeb(sources)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.data.PreferencesHandler
|
||||
import com.rtbishop.look4sat.data.SettingsHandler
|
||||
import com.rtbishop.look4sat.databinding.DialogSourcesBinding
|
||||
import com.rtbishop.look4sat.framework.model.Source
|
||||
import com.rtbishop.look4sat.presentation.setNavResult
|
||||
@@ -36,7 +36,7 @@ import javax.inject.Inject
|
||||
class SourcesDialog : AppCompatDialogFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var preferences: PreferencesHandler
|
||||
lateinit var settings: SettingsHandler
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_sources, group, false)
|
||||
@@ -44,7 +44,7 @@ class SourcesDialog : AppCompatDialogFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, state: Bundle?) {
|
||||
super.onViewCreated(view, state)
|
||||
val sources = preferences.loadDataSources()
|
||||
val sources = settings.loadDataSources()
|
||||
val sourcesAdapter = SourcesAdapter().apply { setSources(sources.map { Source(it) }) }
|
||||
DialogSourcesBinding.bind(view).apply {
|
||||
dialog?.window?.setLayout(
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ buildscript {
|
||||
timber_version = '5.0.1'
|
||||
junit_version = '4.13.2'
|
||||
mockito_version = '4.2.0'
|
||||
leak_canary_version = '2.7'
|
||||
leak_canary_version = '2.8'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
||||
@@ -32,7 +32,7 @@ class DefaultRepository(
|
||||
private val dataParser: DataParser,
|
||||
private val localSource: LocalDataSource,
|
||||
private val remoteSource: RemoteDataSource,
|
||||
private val preferences: PreferencesHandler
|
||||
private val settings: SettingsHandler
|
||||
) : DataRepository {
|
||||
|
||||
override fun getSatelliteItems() = localSource.getSatelliteItems()
|
||||
@@ -48,7 +48,7 @@ class DefaultRepository(
|
||||
override suspend fun updateDataFromWeb(sources: List<String>) {
|
||||
coroutineScope {
|
||||
launch {
|
||||
preferences.saveDataSources(sources)
|
||||
settings.saveDataSources(sources)
|
||||
}
|
||||
launch {
|
||||
val updateTimeMillis = measureTimeMillis {
|
||||
@@ -78,7 +78,7 @@ class DefaultRepository(
|
||||
println("Update from web took $updateTimeMillis ms")
|
||||
}
|
||||
launch {
|
||||
remoteSource.fetchFileStream(preferences.transmittersSource)?.let { inputStream ->
|
||||
remoteSource.fetchFileStream(settings.transmittersSource)?.let { inputStream ->
|
||||
val transmitters = dataParser.parseJSONStream(inputStream)
|
||||
localSource.updateTransmitters(transmitters)
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package com.rtbishop.look4sat.data
|
||||
|
||||
interface PreferencesHandler {
|
||||
interface SettingsHandler {
|
||||
|
||||
val defaultSources: List<String>
|
||||
get() = listOf(
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.model.DataState
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
interface LocationHandler {
|
||||
|
||||
val stationPosition: SharedFlow<DataState<GeoPos>>
|
||||
|
||||
fun getStationPosition(): GeoPos
|
||||
|
||||
fun setStationPosition(latitude: Double, longitude: Double)
|
||||
|
||||
fun setPositionFromLocation()
|
||||
|
||||
fun setPositionFromNet()
|
||||
|
||||
fun setPositionFromGps()
|
||||
|
||||
fun setPositionFromQth(qthString: String)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.rtbishop.look4sat.domain
|
||||
|
||||
import com.rtbishop.look4sat.domain.predict.GeoPos
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
interface LocationProvider {
|
||||
|
||||
val updatedLocation: SharedFlow<GeoPos?>
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package com.rtbishop.look4sat.domain.model
|
||||
|
||||
sealed class DataState<out T> {
|
||||
data class Success<out T>(val data: T) : DataState<T>()
|
||||
data class Error(val error: Throwable) : DataState<Nothing>()
|
||||
object Empty : DataState<Nothing>()
|
||||
data class Error(val message: String?) : DataState<Nothing>()
|
||||
object Loading : DataState<Nothing>()
|
||||
}
|
||||
@@ -84,8 +84,6 @@ class DataParserTest {
|
||||
fun `Given valid data streams parsed results match`() = runTest(testDispatcher) {
|
||||
val csvResult = dataParser.parseCSVStream(validCSVStream)
|
||||
val tleResult = dataParser.parseTLEStream(validTLEStream)
|
||||
println(csvResult)
|
||||
println(tleResult)
|
||||
assert(csvResult == tleResult)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user