Further refactoring, showing passes now after the radarFragment onBack pressed. Trying to decide on the way it all should work.

This commit is contained in:
Arty Bishop committed 2019-11-29 11:01:51 +00:00
1 parent d7a094a1e2
commit 89299d1c3b
2 files changed
+62 -95

No files matched your search

@@ -4,10 +4,8 @@ import android.app.Application
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.location.Location import android.location.Location
import android.util.Log
import androidx.core.content.edit import androidx.core.content.edit
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
@@ -29,95 +27,86 @@ import javax.inject.Inject
class MainViewModel(application: Application) : AndroidViewModel(application) { class MainViewModel(application: Application) : AndroidViewModel(application) {
private val keyLat = "LATITUDE" private val keyLat = "latitude"
private val keyLon = "LONGITUDE" private val keyLon = "longitude"
private val keyHeight = "HEIGHT" private val keyHeight = "height"
private val tleFile = "tles.txt" private val keyHours = "hoursAhead"
private val tag = "myTag" private val keyMaxEl = "maxElevation"
private val tleFileName = "tleFile.txt"
private val preferences = PreferenceManager.getDefaultSharedPreferences(application)
private val locationClient = LocationServices.getFusedLocationProviderClient(application)
val debugMessage = MutableLiveData("")
@Inject @Inject
lateinit var repository: Repository lateinit var repository: Repository
private val preferences = PreferenceManager.getDefaultSharedPreferences(application)
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(application)
init { init {
(application as LookingSatApp).appComponent.inject(this) (application as LookingSatApp).appComponent.inject(this)
} }
private val _debugMessage = MutableLiveData("") val gsp = MutableLiveData<GroundStationPosition>(
val debugMessage: LiveData<String> = _debugMessage
private val _tleMainList = MutableLiveData<List<TLE>>(loadTwoLineElementFile())
val tleMainList: LiveData<List<TLE>> = _tleMainList
private val _tleSelectedMap = MutableLiveData<MutableMap<TLE, Boolean>>(mutableMapOf())
val tleSelectedMap: LiveData<MutableMap<TLE, Boolean>> = _tleSelectedMap
private val _satPassPrefs = MutableLiveData<SatPassPrefs>(
SatPassPrefs(
preferences.getInt("hoursAhead", 8),
preferences.getDouble("maxEl", 20.0)
)
)
val satPassPrefs: LiveData<SatPassPrefs> = _satPassPrefs
private val _gsp = MutableLiveData<GroundStationPosition>(
GroundStationPosition( GroundStationPosition(
preferences.getDouble(keyLat, 0.0), preferences.getDouble(keyLat, 0.0),
preferences.getDouble(keyLon, 0.0), preferences.getDouble(keyLon, 0.0),
preferences.getDouble(keyHeight, 0.0) preferences.getDouble(keyHeight, 0.0)
) )
) )
val gsp: LiveData<GroundStationPosition> = _gsp
var tleMainList = loadTwoLineElementFile()
var tleSelectedMap = mutableMapOf<TLE, Boolean>()
var passPrefs = SatPassPrefs(
preferences.getInt(keyHours, 8),
preferences.getDouble(keyMaxEl, 16.0)
)
private fun loadTwoLineElementFile(): List<TLE> { private fun loadTwoLineElementFile(): List<TLE> {
return try { return try {
TLE.importSat(getApplication<Application>().openFileInput(tleFile)) TLE.importSat(getApplication<Application>().openFileInput(tleFileName))
.sortedWith(compareBy { it.name }) .sortedWith(compareBy { it.name })
} catch (exception: FileNotFoundException) { } catch (exception: FileNotFoundException) {
_debugMessage.postValue("TLE file wasn't found") debugMessage.postValue("TLE file wasn't found")
emptyList() emptyList()
} }
} }
suspend fun getPasses(satMap: Map<TLE, Boolean>, hours: Int, maxEl: Double): List<SatPass> { suspend fun getPasses(): List<SatPass> {
val satPassList = mutableListOf<SatPass>() val satPassList = mutableListOf<SatPass>()
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
satMap.forEach { (tle, value) -> tleSelectedMap.forEach { (tle, value) ->
if (value) { if (value) {
try { try {
val passPredictor = PassPredictor(tle, gsp.value) val predictor = PassPredictor(tle, gsp.value)
val passes = passPredictor.getPasses(Date(), hours, false) val passes = predictor.getPasses(Date(), passPrefs.hoursAhead, false)
passes.forEach { satPassList.add(SatPass(tle, passPredictor, it)) } passes.forEach { satPassList.add(SatPass(tle, predictor, it)) }
} catch (exception: IllegalArgumentException) { } catch (exception: IllegalArgumentException) {
Log.d(tag, "There was a problem with TLE") debugMessage.postValue("There was a problem with TLE")
} catch (exception: SatNotFoundException) { } catch (exception: SatNotFoundException) {
Log.d(tag, "Certain satellites shall not pass") debugMessage.postValue("Certain satellites shall not pass")
} }
} }
} }
satPassList.retainAll { it.pass.maxEl >= maxEl } satPassList.retainAll { it.pass.maxEl >= passPrefs.maxEl }
satPassList.sortBy { it.pass.startTime } satPassList.sortBy { it.pass.startTime }
} }
return satPassList return satPassList
} }
fun updateSelectedSatMap(mutableMap: MutableMap<TLE, Boolean>) { fun updateSelectedSatMap(mutableMap: MutableMap<TLE, Boolean>) {
_tleSelectedMap.postValue(mutableMap) tleSelectedMap = mutableMap
} }
fun updatePassPrefs(hoursAhead: Int, maxEl: Double) { fun updatePassPrefs(hoursAhead: Int, maxEl: Double) {
_satPassPrefs.postValue(SatPassPrefs(hoursAhead, maxEl)) passPrefs = SatPassPrefs(hoursAhead, maxEl)
preferences.edit { preferences.edit {
putInt("hoursAhead", hoursAhead) putInt(keyHours, hoursAhead)
putDouble("maxEl", maxEl) putDouble(keyMaxEl, maxEl)
apply() apply()
} }
} }
fun updateLocation() { fun updateLocation() {
fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? -> locationClient.lastLocation.addOnSuccessListener { location: Location? ->
val lat = location?.latitude ?: 0.0 val lat = location?.latitude ?: 0.0
val lon = location?.longitude ?: 0.0 val lon = location?.longitude ?: 0.0
val height = location?.altitude ?: 0.0 val height = location?.altitude ?: 0.0
@@ -128,8 +117,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
putDouble(keyHeight, height) putDouble(keyHeight, height)
apply() apply()
} }
_gsp.postValue(GroundStationPosition(lat, lon, height)) gsp.postValue(GroundStationPosition(lat, lon, height))
_debugMessage.postValue("Location was updated") debugMessage.postValue("Location was updated")
} }
} }
@@ -137,13 +126,14 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
val stream = repository.fetchTleStream() val stream = repository.fetchTleStream()
getApplication<Application>().openFileOutput(tleFile, Context.MODE_PRIVATE).use { getApplication<Application>().openFileOutput(tleFileName, Context.MODE_PRIVATE)
it.write(stream.readBytes()) .use {
} it.write(stream.readBytes())
_tleMainList.postValue(loadTwoLineElementFile()) }
_debugMessage.postValue("TLE file was updated") tleMainList = loadTwoLineElementFile()
debugMessage.postValue("TLE file was updated")
} catch (exception: IOException) { } catch (exception: IOException) {
_debugMessage.postValue("Couldn't update TLE file") debugMessage.postValue("Couldn't update TLE file")
} }
} }
} }
@@ -152,9 +142,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
repository.updateTransmittersDatabase() repository.updateTransmittersDatabase()
_debugMessage.postValue("Transmitters were updated") debugMessage.postValue("Transmitters were updated")
} catch (exception: IOException) { } catch (exception: IOException) {
_debugMessage.postValue("Couldn't update transmitters") debugMessage.postValue("Couldn't update transmitters")
} }
} }
} }
@@ -17,8 +17,6 @@ import com.github.amsacode.predict4java.TLE
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.rtbishop.lookingsat.MainViewModel import com.rtbishop.lookingsat.MainViewModel
import com.rtbishop.lookingsat.R import com.rtbishop.lookingsat.R
import com.rtbishop.lookingsat.repo.SatPass
import com.rtbishop.lookingsat.repo.SatPassPrefs
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -34,12 +32,14 @@ class SkyFragment : Fragment() {
private lateinit var btnPassPrefs: ImageButton private lateinit var btnPassPrefs: ImageButton
private lateinit var progressBar: ProgressBar private lateinit var progressBar: ProgressBar
private lateinit var fab: FloatingActionButton private lateinit var fab: FloatingActionButton
private lateinit var tleMainList: List<TLE>
private var selectedSatMap: MutableMap<TLE, Boolean> = mutableMapOf()
private lateinit var satPassPrefs: SatPassPrefs
private lateinit var aosTimer: CountDownTimer private lateinit var aosTimer: CountDownTimer
private var isTimerSet: Boolean = false private var isTimerSet: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? { ): View? {
@@ -49,12 +49,9 @@ class SkyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupViews(view) setupViews(view)
setupObservers()
resetTimer()
} }
private fun setupViews(view: View) { private fun setupViews(view: View) {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
timeToAos = (activity as MainActivity).findViewById(R.id.toolbar_time_to_aos) timeToAos = (activity as MainActivity).findViewById(R.id.toolbar_time_to_aos)
btnPassPrefs = (activity as MainActivity).findViewById(R.id.toolbar_btn_refresh) btnPassPrefs = (activity as MainActivity).findViewById(R.id.toolbar_btn_refresh)
progressBar = view.findViewById(R.id.sky_progressbar) progressBar = view.findViewById(R.id.sky_progressbar)
@@ -66,29 +63,7 @@ class SkyFragment : Fragment() {
btnPassPrefs.setOnClickListener { showSatPassPrefsDialog() } btnPassPrefs.setOnClickListener { showSatPassPrefsDialog() }
fab.setOnClickListener { showSelectSatDialog() } fab.setOnClickListener { showSelectSatDialog() }
satPassPrefs = viewModel.satPassPrefs.value ?: SatPassPrefs(8, 20.0) viewModel.gsp.observe(this, Observer { calculatePasses() })
}
private fun setupObservers() {
viewModel.tleMainList.observe(this, Observer {
tleMainList = it
selectedSatMap.clear()
viewModel.updateSelectedSatMap(selectedSatMap)
})
viewModel.tleSelectedMap.observe(this, Observer {
selectedSatMap = it
calculatePasses()
})
viewModel.satPassPrefs.observe(this, Observer {
satPassPrefs = it
calculatePasses()
})
viewModel.gsp.observe(this, Observer {
calculatePasses()
})
} }
private fun showSatPassPrefsDialog() { private fun showSatPassPrefsDialog() {
@@ -96,8 +71,8 @@ class SkyFragment : Fragment() {
val satPassPrefView = View.inflate(context, R.layout.sat_pass_pref, null) val satPassPrefView = View.inflate(context, R.layout.sat_pass_pref, null)
val etHours = satPassPrefView.findViewById<EditText>(R.id.pass_pref_et_hours) val etHours = satPassPrefView.findViewById<EditText>(R.id.pass_pref_et_hours)
val etMaxEl = satPassPrefView.findViewById<EditText>(R.id.pass_pref_et_maxEl) val etMaxEl = satPassPrefView.findViewById<EditText>(R.id.pass_pref_et_maxEl)
etHours.setText(viewModel.satPassPrefs.value?.hoursAhead.toString()) etHours.setText(viewModel.passPrefs.hoursAhead.toString())
etMaxEl.setText(viewModel.satPassPrefs.value?.maxEl.toString()) etMaxEl.setText(viewModel.passPrefs.maxEl.toString())
val builder = AlertDialog.Builder(context) val builder = AlertDialog.Builder(context)
builder.setTitle(getString(R.string.title_pass_pref)) builder.setTitle(getString(R.string.title_pass_pref))
@@ -105,6 +80,7 @@ class SkyFragment : Fragment() {
val hoursAhead = etHours.text.toString().toInt() val hoursAhead = etHours.text.toString().toInt()
val maxEl = etMaxEl.text.toString().toDouble() val maxEl = etMaxEl.text.toString().toDouble()
viewModel.updatePassPrefs(hoursAhead, maxEl) viewModel.updatePassPrefs(hoursAhead, maxEl)
calculatePasses()
} }
.setNegativeButton(getString(R.string.btn_cancel)) { dialog, _ -> .setNegativeButton(getString(R.string.btn_cancel)) { dialog, _ ->
dialog.cancel() dialog.cancel()
@@ -115,10 +91,11 @@ class SkyFragment : Fragment() {
} }
private fun showSelectSatDialog() { private fun showSelectSatDialog() {
val tleMainList: List<TLE> = viewModel.tleMainList
val tleNameArray = arrayOfNulls<String>(tleMainList.size) val tleNameArray = arrayOfNulls<String>(tleMainList.size)
val tleCheckedArray = BooleanArray(tleMainList.size) val tleCheckedArray = BooleanArray(tleMainList.size)
val builder = AlertDialog.Builder(activity as MainActivity) val selectedSatMap = viewModel.tleSelectedMap
val selectionMap = mutableMapOf<TLE, Boolean>() val currentSelectionMap = mutableMapOf<TLE, Boolean>()
if (selectedSatMap.isEmpty()) { if (selectedSatMap.isEmpty()) {
tleMainList.withIndex().forEach { (position, tle) -> tleMainList.withIndex().forEach { (position, tle) ->
@@ -132,14 +109,16 @@ class SkyFragment : Fragment() {
} }
} }
val builder = AlertDialog.Builder(activity as MainActivity)
builder.setTitle(getString(R.string.title_select_sat)) builder.setTitle(getString(R.string.title_select_sat))
.setMultiChoiceItems(tleNameArray, tleCheckedArray) { _, which, isChecked -> .setMultiChoiceItems(tleNameArray, tleCheckedArray) { _, which, isChecked ->
selectionMap[tleMainList[which]] = isChecked currentSelectionMap[tleMainList[which]] = isChecked
} }
.setPositiveButton(getString(R.string.btn_ok)) { _, _ -> .setPositiveButton(getString(R.string.btn_ok)) { _, _ ->
for ((tle, value) in selectionMap) { for ((tle, value) in currentSelectionMap) {
selectedSatMap[tle] = value selectedSatMap[tle] = value
viewModel.updateSelectedSatMap(selectedSatMap) viewModel.updateSelectedSatMap(selectedSatMap)
calculatePasses()
} }
} }
.setNegativeButton(getString(R.string.btn_cancel)) { dialog, _ -> .setNegativeButton(getString(R.string.btn_cancel)) { dialog, _ ->
@@ -148,20 +127,18 @@ class SkyFragment : Fragment() {
.setNeutralButton(getString(R.string.btn_clear)) { _, _ -> .setNeutralButton(getString(R.string.btn_clear)) { _, _ ->
selectedSatMap.clear() selectedSatMap.clear()
viewModel.updateSelectedSatMap(selectedSatMap) viewModel.updateSelectedSatMap(selectedSatMap)
calculatePasses()
} }
.create() .create()
.show() .show()
} }
private fun calculatePasses() { private fun calculatePasses() {
val hoursAhead = satPassPrefs.hoursAhead
val maxEl = satPassPrefs.maxEl
var satPassList: List<SatPass>
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
recViewFuture.visibility = View.INVISIBLE recViewFuture.visibility = View.INVISIBLE
progressBar.visibility = View.VISIBLE progressBar.visibility = View.VISIBLE
progressBar.isIndeterminate = true progressBar.isIndeterminate = true
satPassList = viewModel.getPasses(selectedSatMap, hoursAhead, maxEl) val satPassList = viewModel.getPasses()
recAdapterFuture = SatPassAdapter(satPassList) recAdapterFuture = SatPassAdapter(satPassList)
recViewFuture.adapter = recAdapterFuture recViewFuture.adapter = recAdapterFuture
progressBar.isIndeterminate = false progressBar.isIndeterminate = false