mirror of
https://github.com/atsunatsu/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
+462
-557
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>)
|
suspend fun insertEntries(entries: List<SatEntry>)
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
suspend fun updateEntries(entries: List<SatEntry>) {
|
suspend fun updateEntries(entries: List<SatEntry>, cleanup: Boolean = false) {
|
||||||
val entriesSelection = getEntriesSelection()
|
val entriesSelection = getEntriesSelection()
|
||||||
deleteEntries()
|
if (cleanup) deleteEntries()
|
||||||
insertEntries(entries)
|
insertEntries(entries)
|
||||||
restoreSelection(entriesSelection, true)
|
restoreSelection(entriesSelection, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,15 +19,16 @@ package com.rtbishop.look4sat.injection
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.location.LocationManager
|
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.rtbishop.look4sat.data.DefaultRepository
|
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.DataParser
|
||||||
import com.rtbishop.look4sat.domain.DataReporter
|
import com.rtbishop.look4sat.domain.DataReporter
|
||||||
import com.rtbishop.look4sat.domain.DataRepository
|
import com.rtbishop.look4sat.domain.DataRepository
|
||||||
|
import com.rtbishop.look4sat.domain.LocationHandler
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
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.LocalSource
|
||||||
import com.rtbishop.look4sat.framework.local.SatelliteDb
|
import com.rtbishop.look4sat.framework.local.SatelliteDb
|
||||||
import com.rtbishop.look4sat.framework.remote.RemoteSource
|
import com.rtbishop.look4sat.framework.remote.RemoteSource
|
||||||
@@ -46,7 +47,7 @@ object CoreModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideSatelliteRepo(
|
fun provideSatelliteRepo(
|
||||||
preferencesHandler: PreferencesHandler,
|
settingsHandler: SettingsHandler,
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
@IoDispatcher ioDispatcher: CoroutineDispatcher,
|
||||||
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
|
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher
|
||||||
@@ -56,16 +57,22 @@ object CoreModule {
|
|||||||
.fallbackToDestructiveMigration().build()
|
.fallbackToDestructiveMigration().build()
|
||||||
val localSource = LocalSource(db.entriesDao(), db.transmittersDao())
|
val localSource = LocalSource(db.entriesDao(), db.transmittersDao())
|
||||||
val remoteSource = RemoteSource(ioDispatcher)
|
val remoteSource = RemoteSource(ioDispatcher)
|
||||||
return DefaultRepository(dataParser, localSource, remoteSource, preferencesHandler)
|
return DefaultRepository(dataParser, localSource, remoteSource, settingsHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun providePreferencesHandler(
|
fun provideSettingsHandler(sharedPreferences: SharedPreferences): SettingsHandler {
|
||||||
locationManager: LocationManager,
|
return SettingsProvider(sharedPreferences)
|
||||||
sharedPreferences: SharedPreferences
|
}
|
||||||
): PreferencesHandler {
|
|
||||||
return PreferencesSource(locationManager, sharedPreferences)
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideLocationHandler(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
|
settingsProvider: SettingsProvider
|
||||||
|
): LocationHandler {
|
||||||
|
return LocationProvider(context, settingsProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -89,8 +89,6 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
|||||||
val message = getString(R.string.entries_update_error)
|
val message = getString(R.string.entries_update_error)
|
||||||
Snackbar.make(requireView(), message, Snackbar.LENGTH_SHORT).show()
|
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.DataRepository
|
||||||
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 com.rtbishop.look4sat.framework.PreferencesSource
|
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -34,14 +34,14 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class EntriesViewModel @Inject constructor(
|
class EntriesViewModel @Inject constructor(
|
||||||
private val preferences: PreferencesSource,
|
private val preferences: SettingsProvider,
|
||||||
private val resolver: ContentResolver,
|
private val resolver: ContentResolver,
|
||||||
private val dataRepository: DataRepository
|
private val dataRepository: DataRepository
|
||||||
) : ViewModel(), SearchView.OnQueryTextListener, EntriesAdapter.EntriesClickListener {
|
) : ViewModel(), SearchView.OnQueryTextListener, EntriesAdapter.EntriesClickListener {
|
||||||
|
|
||||||
private val coroutineHandler = CoroutineExceptionHandler { _, throwable ->
|
private val coroutineHandler = CoroutineExceptionHandler { _, throwable ->
|
||||||
Timber.d(throwable)
|
Timber.d(throwable)
|
||||||
_satData.value = DataState.Error(throwable)
|
_satData.value = DataState.Error(null)
|
||||||
}
|
}
|
||||||
private val transModes = MutableLiveData(preferences.loadModesSelection())
|
private val transModes = MutableLiveData(preferences.loadModesSelection())
|
||||||
private val currentQuery = MutableLiveData(String())
|
private val currentQuery = MutableLiveData(String())
|
||||||
@@ -64,7 +64,7 @@ class EntriesViewModel @Inject constructor(
|
|||||||
resolver.openInputStream(uri)?.use { stream ->
|
resolver.openInputStream(uri)?.use { stream ->
|
||||||
dataRepository.updateDataFromFile(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.GeoPos
|
||||||
import com.rtbishop.look4sat.domain.predict.Predictor
|
import com.rtbishop.look4sat.domain.predict.Predictor
|
||||||
import com.rtbishop.look4sat.domain.predict.Satellite
|
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 dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -35,7 +35,7 @@ import kotlin.math.min
|
|||||||
class MapViewModel @Inject constructor(
|
class MapViewModel @Inject constructor(
|
||||||
private val dataRepository: DataRepository,
|
private val dataRepository: DataRepository,
|
||||||
private val predictor: Predictor,
|
private val predictor: Predictor,
|
||||||
preferences: PreferencesSource,
|
preferences: SettingsProvider,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val stationPos = preferences.loadStationPosition()
|
private val stationPos = preferences.loadStationPosition()
|
||||||
|
|||||||
+10
-12
@@ -17,10 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.passesScreen
|
package com.rtbishop.look4sat.presentation.passesScreen
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
@@ -43,15 +41,15 @@ import java.util.*
|
|||||||
class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener {
|
class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener {
|
||||||
|
|
||||||
private val passesViewModel: PassesViewModel by viewModels()
|
private val passesViewModel: PassesViewModel by viewModels()
|
||||||
private val permReqContract = ActivityResultContracts.RequestMultiplePermissions()
|
// private val permReqContract = ActivityResultContracts.RequestMultiplePermissions()
|
||||||
private val locPermFine = Manifest.permission.ACCESS_FINE_LOCATION
|
// private val locPermFine = Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
private val locPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
// private val locPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
private val locPermReq = registerForActivityResult(permReqContract) { permissions ->
|
// private val locPermReq = registerForActivityResult(permReqContract) { permissions ->
|
||||||
when {
|
// when {
|
||||||
permissions[locPermFine] == true -> passesViewModel.triggerInitialSetup()
|
// permissions[locPermFine] == true -> passesViewModel.triggerInitialSetup()
|
||||||
permissions[locPermCoarse] == true -> passesViewModel.triggerInitialSetup()
|
// permissions[locPermCoarse] == true -> passesViewModel.triggerInitialSetup()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@@ -88,7 +86,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
|||||||
handleNewPasses(passesResult, passesAdapter, binding)
|
handleNewPasses(passesResult, passesAdapter, binding)
|
||||||
})
|
})
|
||||||
passesViewModel.isFirstLaunchDone.observe(viewLifecycleOwner, { setupDone ->
|
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 ->
|
getNavResult<Pair<Int, Double>>(R.id.nav_passes, "prefs") { prefs ->
|
||||||
passesViewModel.forceCalculation(prefs.first, prefs.second)
|
passesViewModel.forceCalculation(prefs.first, prefs.second)
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ 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.databinding.DialogPassesBinding
|
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 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 PassesPrefsDialog : AppCompatDialogFragment() {
|
class PassesPrefsDialog : AppCompatDialogFragment() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: PreferencesSource
|
lateinit var preferences: SettingsProvider
|
||||||
|
|
||||||
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_passes, group, false)
|
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.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
|
||||||
import com.rtbishop.look4sat.framework.PreferencesSource
|
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -34,7 +34,7 @@ import javax.inject.Inject
|
|||||||
class PassesViewModel @Inject constructor(
|
class PassesViewModel @Inject constructor(
|
||||||
private val dataRepository: DataRepository,
|
private val dataRepository: DataRepository,
|
||||||
private val predictor: Predictor,
|
private val predictor: Predictor,
|
||||||
private val preferences: PreferencesSource
|
private val preferences: SettingsProvider
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _passes = MutableLiveData<DataState<List<SatPass>>>()
|
private val _passes = MutableLiveData<DataState<List<SatPass>>>()
|
||||||
@@ -66,7 +66,7 @@ class PassesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun triggerInitialSetup() {
|
fun triggerInitialSetup() {
|
||||||
preferences.updatePositionFromGPS()
|
// preferences.updatePositionFromGPS()
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
// _passes.postValue(DataState.Loading)
|
// _passes.postValue(DataState.Loading)
|
||||||
// val satellites = dataRepository.getSelectedSatellites()
|
// val satellites = dataRepository.getSelectedSatellites()
|
||||||
@@ -115,8 +115,7 @@ class PassesViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
currentPasses = currentPasses.filter { it.progress < 100 }
|
currentPasses = currentPasses.filter { it.progress < 100 }
|
||||||
val passesCopy = currentPasses.map { it.copy() }
|
val passesCopy = currentPasses.map { it.copy() }
|
||||||
if (passesCopy.isEmpty()) _passes.postValue(DataState.Empty)
|
_passes.postValue(DataState.Success(passesCopy))
|
||||||
else _passes.postValue(DataState.Success(passesCopy))
|
|
||||||
delay(1000)
|
delay(1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import com.rtbishop.look4sat.R
|
|||||||
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.framework.PreferencesSource
|
import com.rtbishop.look4sat.framework.SettingsProvider
|
||||||
import com.rtbishop.look4sat.presentation.ItemDivider
|
import com.rtbishop.look4sat.presentation.ItemDivider
|
||||||
import com.rtbishop.look4sat.presentation.navigateSafe
|
import com.rtbishop.look4sat.presentation.navigateSafe
|
||||||
import com.rtbishop.look4sat.presentation.toTimerString
|
import com.rtbishop.look4sat.presentation.toTimerString
|
||||||
@@ -40,7 +40,7 @@ import javax.inject.Inject
|
|||||||
class RadarFragment : Fragment(R.layout.fragment_radar) {
|
class RadarFragment : Fragment(R.layout.fragment_radar) {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: PreferencesSource
|
lateinit var preferences: SettingsProvider
|
||||||
private val viewModel: RadarViewModel by viewModels()
|
private val viewModel: RadarViewModel by viewModels()
|
||||||
private var radarView: RadarView? = null
|
private var radarView: RadarView? = null
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,17 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.radarScreen
|
package com.rtbishop.look4sat.presentation.radarScreen
|
||||||
|
|
||||||
|
import android.hardware.GeomagneticField
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.rtbishop.look4sat.domain.DataReporter
|
import com.rtbishop.look4sat.domain.DataReporter
|
||||||
import com.rtbishop.look4sat.domain.DataRepository
|
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.Predictor
|
||||||
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.model.Transmitter
|
|
||||||
import com.rtbishop.look4sat.framework.OrientationSource
|
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 com.rtbishop.look4sat.presentation.round
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -37,7 +39,7 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class RadarViewModel @Inject constructor(
|
class RadarViewModel @Inject constructor(
|
||||||
private val orientationSource: OrientationSource,
|
private val orientationSource: OrientationSource,
|
||||||
private val preferences: PreferencesSource,
|
private val preferences: SettingsProvider,
|
||||||
private val predictor: Predictor,
|
private val predictor: Predictor,
|
||||||
private val dataRepository: DataRepository,
|
private val dataRepository: DataRepository,
|
||||||
private val dataReporter: DataReporter
|
private val dataReporter: DataReporter
|
||||||
@@ -71,7 +73,13 @@ class RadarViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) {
|
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) {
|
private fun sendPassData(satPass: SatPass) {
|
||||||
|
|||||||
+112
-139
@@ -18,12 +18,8 @@
|
|||||||
package com.rtbishop.look4sat.presentation.settingsScreen
|
package com.rtbishop.look4sat.presentation.settingsScreen
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.ContentResolver
|
||||||
import android.content.Intent
|
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.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
@@ -31,170 +27,161 @@ import android.view.View
|
|||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.lifecycle.asLiveData
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.rtbishop.look4sat.BuildConfig
|
import com.rtbishop.look4sat.BuildConfig
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.databinding.FragmentSettingsBinding
|
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 com.rtbishop.look4sat.presentation.*
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SettingsFragment : Fragment(R.layout.fragment_settings), LocationListener {
|
class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: PreferencesSource
|
lateinit var preferences: SettingsProvider
|
||||||
|
|
||||||
private val viewModel: SettingsViewModel by viewModels()
|
@Inject
|
||||||
private val locPermFine = Manifest.permission.ACCESS_FINE_LOCATION
|
lateinit var locationHandler: LocationHandler
|
||||||
private val locPermCoarse = Manifest.permission.ACCESS_COARSE_LOCATION
|
|
||||||
private val contentContract = ActivityResultContracts.GetContent()
|
@Inject
|
||||||
private val filePicker = registerForActivityResult(contentContract) { uri ->
|
lateinit var resolver: ContentResolver
|
||||||
uri?.let { viewModel.updateDataFromFile(uri) }
|
|
||||||
}
|
@Inject
|
||||||
private val permReqContract = ActivityResultContracts.RequestMultiplePermissions()
|
lateinit var dataRepository: DataRepository
|
||||||
private val locPermReq = registerForActivityResult(permReqContract) { permissions ->
|
|
||||||
|
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 {
|
when {
|
||||||
permissions[locPermFine] == true -> updatePositionFromGPS()
|
permissions[locationFine] == true -> locationHandler.setPositionFromLocation()
|
||||||
permissions[locPermCoarse] == true -> updatePositionFromGPS()
|
permissions[locationCoarse] == true -> locationHandler.setPositionFromNet()
|
||||||
else -> showSnack(getString(R.string.pref_pos_gps_error))
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
_binding = FragmentSettingsBinding.bind(view)
|
val settingsBinding = FragmentSettingsBinding.bind(view)
|
||||||
_binding?.prefsBack?.setOnClickListener { findNavController().navigateUp() }
|
settingsBinding.prefsBack.setOnClickListener { findNavController().navigateUp() }
|
||||||
setupAboutCard()
|
setupAboutCard(settingsBinding)
|
||||||
setupDataCard()
|
setupDataCard(settingsBinding)
|
||||||
setupLocationCard()
|
setupLocationCard(settingsBinding)
|
||||||
setupTrackingCard()
|
setupTrackingCard(settingsBinding)
|
||||||
setupOtherCard()
|
setupOtherCard(settingsBinding)
|
||||||
setupWarrantyCard()
|
setupWarrantyCard(settingsBinding)
|
||||||
}
|
locationHandler.stationPosition.asLiveData().observe(viewLifecycleOwner) { stationPos ->
|
||||||
|
stationPos?.let { handleStationPosition(it) }
|
||||||
private fun setupAboutCard() {
|
|
||||||
_binding?.prefsInfo?.let { binding ->
|
|
||||||
binding.aboutVersion.text =
|
|
||||||
String.format(getString(R.string.about_version), BuildConfig.VERSION_NAME)
|
|
||||||
binding.aboutBtnGithub.setOnClickListener {
|
|
||||||
gotoUrl("https://github.com/rt-bishop/Look4Sat/")
|
|
||||||
}
|
|
||||||
binding.aboutBtnDonate.setOnClickListener {
|
|
||||||
gotoUrl("https://www.buymeacoffee.com/rtbishop")
|
|
||||||
}
|
|
||||||
binding.aboutBtnFdroid.setOnClickListener {
|
|
||||||
gotoUrl("https://f-droid.org/en/packages/com.rtbishop.look4sat/")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupDataCard() {
|
private fun setupAboutCard(binding: FragmentSettingsBinding) {
|
||||||
_binding?.prefsData?.let { binding ->
|
binding.prefsInfo.aboutVersion.text =
|
||||||
binding.updateBtnWeb.setOnClickListener {
|
String.format(getString(R.string.about_version), BuildConfig.VERSION_NAME)
|
||||||
findNavController().navigateSafe(R.id.action_prefs_to_sources)
|
binding.prefsInfo.aboutBtnGithub.setOnClickListener {
|
||||||
}
|
gotoUrl("https://github.com/rt-bishop/Look4Sat/")
|
||||||
binding.updateBtnFile.setOnClickListener { filePicker.launch("*/*") }
|
}
|
||||||
getNavResult<List<String>>(R.id.nav_prefs, "sources") { sources ->
|
binding.prefsInfo.aboutBtnDonate.setOnClickListener {
|
||||||
viewModel.updateDataFromWeb(sources)
|
gotoUrl("https://www.buymeacoffee.com/rtbishop")
|
||||||
}
|
}
|
||||||
|
binding.prefsInfo.aboutBtnFdroid.setOnClickListener {
|
||||||
|
gotoUrl("https://f-droid.org/en/packages/com.rtbishop.look4sat/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupLocationCard() {
|
private fun setupDataCard(binding: FragmentSettingsBinding) {
|
||||||
_binding?.prefsLocation?.let { binding ->
|
binding.prefsData.updateBtnFile.setOnClickListener { contentRequest.launch("*/*") }
|
||||||
binding.locationBtnGps.setOnClickListener {
|
binding.prefsData.updateBtnWeb.setOnClickListener {
|
||||||
updatePositionFromGPS()
|
findNavController().navigateSafe(R.id.action_prefs_to_sources)
|
||||||
}
|
}
|
||||||
binding.locationBtnQth.setOnClickListener {
|
getNavResult<List<String>>(R.id.nav_prefs, "sources") { sources ->
|
||||||
val editText = EditText(requireActivity())
|
lifecycleScope.launchWhenResumed { dataRepository.updateDataFromWeb(sources) }
|
||||||
AlertDialog.Builder(requireContext())
|
|
||||||
.setTitle("Title")
|
|
||||||
.setEditText(editText)
|
|
||||||
.setPositiveButton("OK") { _, _ ->
|
|
||||||
val editTextInput = editText.text.toString()
|
|
||||||
updatePositionFromQth(editTextInput)
|
|
||||||
}
|
|
||||||
.setNeutralButton("Cancel", null)
|
|
||||||
.create()
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupTrackingCard() {
|
private fun setupLocationCard(binding: FragmentSettingsBinding) {
|
||||||
_binding?.prefsTracking?.let { binding ->
|
binding.prefsLocation.locationBtnGps.setOnClickListener {
|
||||||
binding.trackingSwitch.apply {
|
locationRequest.launch(arrayOf(locationFine, locationCoarse))
|
||||||
isChecked = preferences.getRotatorEnabled()
|
}
|
||||||
binding.trackingIp.isEnabled = isChecked
|
binding.prefsLocation.locationBtnQth.setOnClickListener {
|
||||||
binding.trackingIpEdit.setText(preferences.getRotatorIp())
|
val editText = EditText(requireActivity())
|
||||||
binding.trackingPort.isEnabled = isChecked
|
AlertDialog.Builder(requireContext())
|
||||||
binding.trackingPortEdit.setText(preferences.getRotatorPort())
|
.setTitle("Title")
|
||||||
setOnCheckedChangeListener { _, isChecked ->
|
.setEditText(editText)
|
||||||
preferences.setRotatorEnabled(isChecked)
|
.setPositiveButton("OK") { _, _ ->
|
||||||
binding.trackingIp.isEnabled = isChecked
|
val editTextInput = editText.text.toString()
|
||||||
binding.trackingPort.isEnabled = isChecked
|
locationHandler.setPositionFromQth(editTextInput)
|
||||||
}
|
}
|
||||||
}
|
.setNeutralButton("Cancel", null)
|
||||||
binding.trackingIpEdit.doOnTextChanged { text, _, _, _ ->
|
.create()
|
||||||
if (text.toString().isValidIPv4()) preferences.setRotatorIp(text.toString())
|
.show()
|
||||||
}
|
|
||||||
binding.trackingPortEdit.doOnTextChanged { text, _, _, _ ->
|
|
||||||
if (text.toString().isValidPort()) preferences.setRotatorPort(text.toString())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupOtherCard() {
|
private fun setupTrackingCard(binding: FragmentSettingsBinding) {
|
||||||
_binding?.prefsOther?.let { binding ->
|
binding.prefsTracking.trackingSwitch.apply {
|
||||||
binding.otherSwitchUtc.apply {
|
isChecked = preferences.getRotatorEnabled()
|
||||||
isChecked = preferences.getUseUTC()
|
binding.prefsTracking.trackingIp.isEnabled = isChecked
|
||||||
setOnCheckedChangeListener { _, isChecked -> preferences.setUseUTC(isChecked) }
|
binding.prefsTracking.trackingIpEdit.setText(preferences.getRotatorIp())
|
||||||
}
|
binding.prefsTracking.trackingPort.isEnabled = isChecked
|
||||||
binding.otherSwitchSweep.apply {
|
binding.prefsTracking.trackingPortEdit.setText(preferences.getRotatorPort())
|
||||||
isChecked = preferences.getShowSweep()
|
setOnCheckedChangeListener { _, isChecked ->
|
||||||
setOnCheckedChangeListener { _, isChecked -> preferences.setShowSweep(isChecked) }
|
preferences.setRotatorEnabled(isChecked)
|
||||||
}
|
binding.prefsTracking.trackingIp.isEnabled = isChecked
|
||||||
binding.otherSwitchSensors.apply {
|
binding.prefsTracking.trackingPort.isEnabled = isChecked
|
||||||
isChecked = preferences.getUseCompass()
|
|
||||||
setOnCheckedChangeListener { _, isChecked -> preferences.setUseCompass(isChecked) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
binding.prefsTracking.trackingIpEdit.doOnTextChanged { text, _, _, _ ->
|
||||||
|
if (text.toString().isValidIPv4()) preferences.setRotatorIp(text.toString())
|
||||||
|
}
|
||||||
|
binding.prefsTracking.trackingPortEdit.doOnTextChanged { text, _, _, _ ->
|
||||||
|
if (text.toString().isValidPort()) preferences.setRotatorPort(text.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupWarrantyCard() {
|
private fun setupOtherCard(binding: FragmentSettingsBinding) {
|
||||||
_binding?.prefsWarranty?.let { binding ->
|
binding.prefsOther.otherSwitchUtc.apply {
|
||||||
binding.warrantyThanks.movementMethod = LinkMovementMethod.getInstance()
|
isChecked = preferences.getUseUTC()
|
||||||
binding.warrantyLicense.movementMethod = LinkMovementMethod.getInstance()
|
setOnCheckedChangeListener { _, isChecked -> preferences.setUseUTC(isChecked) }
|
||||||
|
}
|
||||||
|
binding.prefsOther.otherSwitchSweep.apply {
|
||||||
|
isChecked = preferences.getShowSweep()
|
||||||
|
setOnCheckedChangeListener { _, isChecked -> preferences.setShowSweep(isChecked) }
|
||||||
|
}
|
||||||
|
binding.prefsOther.otherSwitchSensors.apply {
|
||||||
|
isChecked = preferences.getUseCompass()
|
||||||
|
setOnCheckedChangeListener { _, isChecked -> preferences.setUseCompass(isChecked) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePositionFromGPS() {
|
private fun setupWarrantyCard(binding: FragmentSettingsBinding) {
|
||||||
val manager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
binding.prefsWarranty.warrantyThanks.movementMethod = LinkMovementMethod.getInstance()
|
||||||
val provider = LocationManager.NETWORK_PROVIDER
|
binding.prefsWarranty.warrantyLicense.movementMethod = LinkMovementMethod.getInstance()
|
||||||
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) {
|
private fun handleStationPosition(position: DataState<GeoPos>) {
|
||||||
if (preferences.updatePositionFromQTH(qthString)) {
|
when (position) {
|
||||||
showSnack(getString(R.string.pref_pos_success))
|
is DataState.Success -> showSnack(getString(R.string.pref_pos_success))
|
||||||
} else {
|
is DataState.Error -> showSnack(position.message.toString())
|
||||||
showSnack(getString(R.string.pref_pos_qth_error))
|
DataState.Loading -> showSnack("Fetching position")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,18 +192,4 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), LocationListener
|
|||||||
private fun gotoUrl(url: String) {
|
private fun gotoUrl(url: String) {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
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.appcompat.app.AppCompatDialogFragment
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.rtbishop.look4sat.R
|
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.databinding.DialogSourcesBinding
|
||||||
import com.rtbishop.look4sat.framework.model.Source
|
import com.rtbishop.look4sat.framework.model.Source
|
||||||
import com.rtbishop.look4sat.presentation.setNavResult
|
import com.rtbishop.look4sat.presentation.setNavResult
|
||||||
@@ -36,7 +36,7 @@ import javax.inject.Inject
|
|||||||
class SourcesDialog : AppCompatDialogFragment() {
|
class SourcesDialog : AppCompatDialogFragment() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var preferences: PreferencesHandler
|
lateinit var settings: SettingsHandler
|
||||||
|
|
||||||
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_sources, group, false)
|
return inflater.inflate(R.layout.dialog_sources, group, false)
|
||||||
@@ -44,7 +44,7 @@ class SourcesDialog : AppCompatDialogFragment() {
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, state: Bundle?) {
|
override fun onViewCreated(view: View, state: Bundle?) {
|
||||||
super.onViewCreated(view, state)
|
super.onViewCreated(view, state)
|
||||||
val sources = preferences.loadDataSources()
|
val sources = settings.loadDataSources()
|
||||||
val sourcesAdapter = SourcesAdapter().apply { setSources(sources.map { Source(it) }) }
|
val sourcesAdapter = SourcesAdapter().apply { setSources(sources.map { Source(it) }) }
|
||||||
DialogSourcesBinding.bind(view).apply {
|
DialogSourcesBinding.bind(view).apply {
|
||||||
dialog?.window?.setLayout(
|
dialog?.window?.setLayout(
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ buildscript {
|
|||||||
timber_version = '5.0.1'
|
timber_version = '5.0.1'
|
||||||
junit_version = '4.13.2'
|
junit_version = '4.13.2'
|
||||||
mockito_version = '4.2.0'
|
mockito_version = '4.2.0'
|
||||||
leak_canary_version = '2.7'
|
leak_canary_version = '2.8'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class DefaultRepository(
|
|||||||
private val dataParser: DataParser,
|
private val dataParser: DataParser,
|
||||||
private val localSource: LocalDataSource,
|
private val localSource: LocalDataSource,
|
||||||
private val remoteSource: RemoteDataSource,
|
private val remoteSource: RemoteDataSource,
|
||||||
private val preferences: PreferencesHandler
|
private val settings: SettingsHandler
|
||||||
) : DataRepository {
|
) : DataRepository {
|
||||||
|
|
||||||
override fun getSatelliteItems() = localSource.getSatelliteItems()
|
override fun getSatelliteItems() = localSource.getSatelliteItems()
|
||||||
@@ -48,7 +48,7 @@ class DefaultRepository(
|
|||||||
override suspend fun updateDataFromWeb(sources: List<String>) {
|
override suspend fun updateDataFromWeb(sources: List<String>) {
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
launch {
|
launch {
|
||||||
preferences.saveDataSources(sources)
|
settings.saveDataSources(sources)
|
||||||
}
|
}
|
||||||
launch {
|
launch {
|
||||||
val updateTimeMillis = measureTimeMillis {
|
val updateTimeMillis = measureTimeMillis {
|
||||||
@@ -78,7 +78,7 @@ class DefaultRepository(
|
|||||||
println("Update from web took $updateTimeMillis ms")
|
println("Update from web took $updateTimeMillis ms")
|
||||||
}
|
}
|
||||||
launch {
|
launch {
|
||||||
remoteSource.fetchFileStream(preferences.transmittersSource)?.let { inputStream ->
|
remoteSource.fetchFileStream(settings.transmittersSource)?.let { inputStream ->
|
||||||
val transmitters = dataParser.parseJSONStream(inputStream)
|
val transmitters = dataParser.parseJSONStream(inputStream)
|
||||||
localSource.updateTransmitters(transmitters)
|
localSource.updateTransmitters(transmitters)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.data
|
package com.rtbishop.look4sat.data
|
||||||
|
|
||||||
interface PreferencesHandler {
|
interface SettingsHandler {
|
||||||
|
|
||||||
val defaultSources: List<String>
|
val defaultSources: List<String>
|
||||||
get() = listOf(
|
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> {
|
sealed class DataState<out T> {
|
||||||
data class Success<out T>(val data: T) : DataState<T>()
|
data class Success<out T>(val data: T) : DataState<T>()
|
||||||
data class Error(val error: Throwable) : DataState<Nothing>()
|
data class Error(val message: String?) : DataState<Nothing>()
|
||||||
object Empty : DataState<Nothing>()
|
|
||||||
object Loading : DataState<Nothing>()
|
object Loading : DataState<Nothing>()
|
||||||
}
|
}
|
||||||
@@ -84,8 +84,6 @@ class DataParserTest {
|
|||||||
fun `Given valid data streams parsed results match`() = runTest(testDispatcher) {
|
fun `Given valid data streams parsed results match`() = runTest(testDispatcher) {
|
||||||
val csvResult = dataParser.parseCSVStream(validCSVStream)
|
val csvResult = dataParser.parseCSVStream(validCSVStream)
|
||||||
val tleResult = dataParser.parseTLEStream(validTLEStream)
|
val tleResult = dataParser.parseTLEStream(validTLEStream)
|
||||||
println(csvResult)
|
|
||||||
println(tleResult)
|
|
||||||
assert(csvResult == tleResult)
|
assert(csvResult == tleResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user