mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Added entries update states and error handling
This commit is contained in:
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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user