Added entries update states and error handling

This commit is contained in:
Arty Bishop committed 2020-10-18 20:40:58 +01:00
1 parent e9424ad997
commit 628a01b530
7 files changed
+117 -76

No files matched your search

@@ -20,10 +20,11 @@
package com.rtbishop.look4sat
import android.net.Uri
import androidx.hilt.Assisted
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.*
import com.github.amsacode.predict4java.GroundStationPosition
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.data.*
import com.rtbishop.look4sat.repo.EntriesRepo
import com.rtbishop.look4sat.repo.SourcesRepo
@@ -41,34 +42,27 @@ class SharedViewModel @ViewModelInject constructor(
private val prefsManager: PrefsManager,
private val sourcesRepo: SourcesRepo,
private val entriesRepo: EntriesRepo,
private val transmittersRepo: TransmittersRepo,
@Assisted private val savedStateHandle: SavedStateHandle
private val transmittersRepo: TransmittersRepo
) : ViewModel() {
private val _currentTimeMillis = MutableLiveData(System.currentTimeMillis())
private val _appTimer = MutableLiveData(System.currentTimeMillis())
private val _passes = MutableLiveData<Result<MutableList<SatPass>>>()
private val _appEvent = MutableLiveData<Event<Any>>()
private val _appEvent = MutableLiveData<Event<Int>>()
private var selectedEntries = emptyList<SatEntry>()
private var shouldTriggerCalculation = true
init {
if (prefsManager.isFirstLaunch()) {
updateDefaultSourcesAndEntries()
}
if (prefsManager.isFirstLaunch()) updateDefaultSourcesAndEntries()
startApplicationTimer()
}
fun getAppEvent(): LiveData<Event<Int>> = _appEvent
fun getAppTimer(): LiveData<Long> = _appTimer
fun getSources() = sourcesRepo.getSources()
fun getEntries() = entriesRepo.getEntries()
fun getCurrentTimeMillis(): LiveData<Long> = _currentTimeMillis
fun getPasses(): LiveData<Result<MutableList<SatPass>>> = _passes
fun getAppEvent(): LiveData<Event<Any>> = _appEvent
fun getTransmittersForSat(satId: Int) = transmittersRepo.getTransmittersForSat(satId)
fun postAppEvent(event: Event<Any>) {
_appEvent.value = event
}
fun triggerCalculation() {
if (shouldTriggerCalculation) {
shouldTriggerCalculation = false
@@ -76,28 +70,41 @@ class SharedViewModel @ViewModelInject constructor(
}
}
fun setSelectedEntries(entries: List<SatEntry>) {
fun calculatePasses(dateNow: Date = Date(System.currentTimeMillis())) {
shouldTriggerCalculation = false
_passes.value = Result.InProgress
viewModelScope.launch(Dispatchers.Default) {
val passes = mutableListOf<SatPass>()
selectedEntries.forEach { passes.addAll(getPasses(it, dateNow)) }
val filteredPasses = sortList(passes, dateNow)
_passes.postValue(Result.Success(filteredPasses))
}
}
fun setEntries(entries: List<SatEntry>) {
selectedEntries = entries.filter { it.isSelected }
}
fun updateEntriesFromFile(uri: Uri) {
postAppEvent(Event(0))
viewModelScope.launch {
try {
entriesRepo.updateEntriesFromFile(uri)
} catch (exception: Exception) {
postAppEvent(Event(exception))
postAppEvent(Event(1))
}
}
}
fun updateEntriesFromSources(sources: List<TleSource>) {
postAppEvent(Event(0))
viewModelScope.launch {
try {
sourcesRepo.updateSources(sources)
entriesRepo.updateEntriesFromSources(sources)
transmittersRepo.updateTransmitters()
} catch (exception: Exception) {
postAppEvent(Event(exception))
postAppEvent(Event(1))
}
}
}
@@ -110,20 +117,41 @@ class SharedViewModel @ViewModelInject constructor(
}
}
fun calculatePasses(dateNow: Date = Date(System.currentTimeMillis())) {
shouldTriggerCalculation = false
_passes.value = Result.InProgress
viewModelScope.launch(Dispatchers.Default) {
val passes = mutableListOf<SatPass>()
selectedEntries.forEach {
passes.addAll(getPassesForEntries(it, dateNow, prefsManager.getStationPosition()))
private fun postAppEvent(event: Event<Int>) {
_appEvent.value = event
}
private fun startApplicationTimer(tickMillis: Long = 1000L) {
viewModelScope.launch(Dispatchers.IO) {
while (true) {
delay(tickMillis)
_appTimer.postValue(System.currentTimeMillis())
}
val filteredPasses =
filterAndSortPasses(passes, dateNow, prefsManager.getPassPrefs().hoursAhead)
_passes.postValue(Result.Success(filteredPasses))
}
}
private fun getPasses(entry: SatEntry, dateNow: Date): MutableList<SatPass> {
val gsp = prefsManager.getStationPosition()
val predictor = PassPredictor(entry.tle, gsp)
val hoursAhead = prefsManager.getPassPrefs().hoursAhead
val passes = predictor.getPasses(dateNow, hoursAhead, true)
val passList = passes.map { SatPass(entry.tle, predictor, it) }
return passList as MutableList<SatPass>
}
private fun sortList(passes: MutableList<SatPass>, dateNow: Date): MutableList<SatPass> {
val hoursAhead = prefsManager.getPassPrefs().hoursAhead
val dateFuture = Calendar.getInstance().apply {
this.time = dateNow
this.add(Calendar.HOUR, hoursAhead)
}.time
passes.removeAll { it.pass.startTime.after(dateFuture) }
passes.removeAll { it.pass.endTime.before(dateNow) }
passes.removeAll { it.pass.maxEl < prefsManager.getPassPrefs().minEl }
passes.sortBy { it.pass.startTime }
return passes
}
private fun updateDefaultSourcesAndEntries() {
val defaultTleSources = listOf(
TleSource("https://celestrak.com/NORAD/elements/active.txt"),
@@ -131,43 +159,4 @@ class SharedViewModel @ViewModelInject constructor(
)
updateEntriesFromSources(defaultTleSources)
}
private fun startApplicationTimer(tickMillis: Long = 1000L) {
viewModelScope.launch(Dispatchers.IO) {
while (true) {
delay(tickMillis)
_currentTimeMillis.postValue(System.currentTimeMillis())
}
}
}
private fun getPassesForEntries(
entry: SatEntry,
dateNow: Date,
gsp: GroundStationPosition
): MutableList<SatPass> {
val predictor = PassPredictor(entry.tle, gsp)
val hoursAhead = prefsManager.getPassPrefs().hoursAhead
val passes = predictor.getPasses(dateNow, hoursAhead, true)
val passList = passes.map { SatPass(entry.tle, predictor, it) }
return passList as MutableList<SatPass>
}
private fun filterAndSortPasses(
passes: MutableList<SatPass>,
dateNow: Date,
hoursAhead: Int
): MutableList<SatPass> {
val dateFuture = Calendar.getInstance().let {
it.time = dateNow
it.add(Calendar.HOUR, hoursAhead)
it.time
}
val minEl = prefsManager.getPassPrefs().minEl
passes.removeAll { it.pass.startTime.after(dateFuture) }
passes.removeAll { it.pass.endTime.before(dateNow) }
passes.removeAll { it.pass.maxEl < minEl }
passes.sortBy { it.pass.startTime }
return passes
}
}
@@ -55,12 +55,20 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
private fun setupObservers() {
viewModel.getSources().observe(viewLifecycleOwner, { tleSources = it })
viewModel.getEntries().observe(viewLifecycleOwner, {
viewModel.setSelectedEntries(it)
entriesAdapter.setEntries(it as MutableList<SatEntry>)
if (it.isNullOrEmpty()) setError()
else {
viewModel.setEntries(it)
entriesAdapter.setEntries(it as MutableList<SatEntry>)
setLoaded()
}
})
viewModel.getAppEvent().observe(viewLifecycleOwner, { event ->
event.getContentIfNotHandled()?.let {
getString(R.string.error_updating_data).snack(requireView())
if (it == 0) setLoading()
else if (it == 1) {
getString(R.string.error_updating_data).snack(requireView())
setError()
}
}
})
}
@@ -77,6 +85,24 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
}
}
private fun setLoaded() {
binding.entriesError.visibility = View.INVISIBLE
binding.entriesProgress.visibility = View.INVISIBLE
binding.entriesRecycler.visibility = View.VISIBLE
}
private fun setLoading() {
binding.entriesError.visibility = View.INVISIBLE
binding.entriesRecycler.visibility = View.INVISIBLE
binding.entriesProgress.visibility = View.VISIBLE
}
private fun setError() {
binding.entriesProgress.visibility = View.INVISIBLE
binding.entriesRecycler.visibility = View.INVISIBLE
binding.entriesError.visibility = View.VISIBLE
}
private fun navigateToPasses() {
binding.searchBar.clearFocus()
viewModel.updateEntriesSelection(entriesAdapter.getEntries())
@@ -68,7 +68,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
binding.passesProgress.visibility = View.VISIBLE
}
})
viewModel.getCurrentTimeMillis().observe(viewLifecycleOwner, { currentTime ->
viewModel.getAppTimer().observe(viewLifecycleOwner, { currentTime ->
tickMainTimer(currentTime)
passesAdapter.tickPasses(currentTime)
})
@@ -134,7 +134,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
}
private fun observeTimer() {
viewModel.getCurrentTimeMillis().observe(viewLifecycleOwner, {
viewModel.getAppTimer().observe(viewLifecycleOwner, {
setPassText(it)
polarView?.invalidate()
transmitterAdapter.notifyDataSetChanged()
+25 -1
View File
@@ -39,7 +39,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:elevation="4dp"
android:text="@string/sat_import"
android:text="@string/entries_update"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@+id/import_web"
app:layout_constraintStart_toStartOf="parent"
@@ -94,11 +94,35 @@
android:id="@+id/entries_recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_bar" />
<ProgressBar
android:id="@+id/entries_progress"
style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/entries_bar" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/entries_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/entries_error"
android:textSize="20sp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/entries_bar" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
+2 -1
View File
@@ -104,5 +104,6 @@
<string name="tle_source">Источник TLE</string>
<string name="https_only">Используйте HTTPS</string>
<string name="sat_import">Импорт спутников</string>
<string name="entries_update">Обновить данные TLE:</string>
<string name="entries_error">Пожалуйста, обновите данные TLE</string>
</resources>
+2 -1
View File
@@ -118,7 +118,8 @@
<string name="https_only">Please use HTTPS URLs only</string>
<string name="placeholder" translatable="false">Placeholder</string>
<string name="osmCopyright" translatable="false">Wikimedia maps | Map data © OpenStreetMap contributors</string>
<string name="sat_import">Import satellite data</string>
<string name="timer_zero" translatable="false">00:00:00</string>
<string name="timer_placeholder" translatable="false">-</string>
<string name="entries_update">TLE data update:</string>
<string name="entries_error">Please update TLE data</string>
</resources>