mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
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:
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
|
||||||
|
|||||||
Reference in new issue
Block a user