Swapped SingleLiveEvent for MutableStateFlow
This commit is contained in:
1 parent
d493e3e87b
commit
8134db90d6
7 files changed
+49
-73
No files matched your search
@@ -1,34 +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.data
|
|
||||||
|
|
||||||
class Event<out T>(private val content: T) {
|
|
||||||
|
|
||||||
private var hasBeenHandled = false
|
|
||||||
|
|
||||||
fun getContentIfNotHandled(): T? {
|
|
||||||
return if (hasBeenHandled) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
hasBeenHandled = true
|
|
||||||
content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun peekContent(): T = content
|
|
||||||
}
|
|
||||||
@@ -20,13 +20,18 @@ package com.rtbishop.look4sat.ui
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.github.amsacode.predict4java.Satellite
|
import com.github.amsacode.predict4java.Satellite
|
||||||
import com.rtbishop.look4sat.data.*
|
import com.rtbishop.look4sat.data.Result
|
||||||
|
import com.rtbishop.look4sat.data.SatItem
|
||||||
|
import com.rtbishop.look4sat.data.SatPass
|
||||||
|
import com.rtbishop.look4sat.data.TleSource
|
||||||
import com.rtbishop.look4sat.repository.PrefsRepo
|
import com.rtbishop.look4sat.repository.PrefsRepo
|
||||||
import com.rtbishop.look4sat.repository.SatelliteRepo
|
import com.rtbishop.look4sat.repository.SatelliteRepo
|
||||||
import com.rtbishop.look4sat.utility.getPredictor
|
import com.rtbishop.look4sat.utility.getPredictor
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -37,18 +42,21 @@ class SharedViewModel @Inject constructor(
|
|||||||
private val satelliteRepo: SatelliteRepo,
|
private val satelliteRepo: SatelliteRepo,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _passes = MutableLiveData<Result<MutableList<SatPass>>>()
|
private val _passes = MutableStateFlow<Result<MutableList<SatPass>>>(Result.InProgress)
|
||||||
private val _appEvent = MutableLiveData<Event<Int>>()
|
val passes: LiveData<Result<MutableList<SatPass>>> = _passes.asLiveData()
|
||||||
|
|
||||||
|
private val _satData = MutableStateFlow<Result<List<SatItem>>>(Result.InProgress)
|
||||||
|
val satData: LiveData<Result<List<SatItem>>> = _satData.asLiveData()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (prefsRepo.isFirstLaunch()) {
|
if (prefsRepo.isFirstLaunch()) {
|
||||||
updateDefaultSourcesAndEntries()
|
updateDefaultSourcesAndEntries()
|
||||||
prefsRepo.setFirstLaunchDone()
|
prefsRepo.setFirstLaunchDone()
|
||||||
}
|
}
|
||||||
|
loadDataFromDb()
|
||||||
calculatePasses()
|
calculatePasses()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAppEvent(): LiveData<Event<Int>> = _appEvent
|
|
||||||
fun getAppTimer() = liveData {
|
fun getAppTimer() = liveData {
|
||||||
while (true) {
|
while (true) {
|
||||||
emit(System.currentTimeMillis())
|
emit(System.currentTimeMillis())
|
||||||
@@ -57,10 +65,7 @@ class SharedViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSources() = prefsRepo.loadTleSources()
|
fun getSources() = prefsRepo.loadTleSources()
|
||||||
fun getSatItems() = satelliteRepo.getSatItems().asLiveData()
|
fun getTransmittersForSat(satId: Int) = satelliteRepo.getTransmittersForSat(satId).asLiveData()
|
||||||
fun getPasses(): LiveData<Result<MutableList<SatPass>>> = _passes
|
|
||||||
fun getTransmittersForSat(satId: Int) =
|
|
||||||
satelliteRepo.getTransmittersForSat(satId).asLiveData()
|
|
||||||
|
|
||||||
fun calculatePasses(dateNow: Date = Date(System.currentTimeMillis())) {
|
fun calculatePasses(dateNow: Date = Date(System.currentTimeMillis())) {
|
||||||
_passes.value = Result.InProgress
|
_passes.value = Result.InProgress
|
||||||
@@ -70,30 +75,30 @@ class SharedViewModel @Inject constructor(
|
|||||||
passes.addAll(getPasses(satellite, dateNow))
|
passes.addAll(getPasses(satellite, dateNow))
|
||||||
}
|
}
|
||||||
val filteredPasses = sortList(passes, dateNow)
|
val filteredPasses = sortList(passes, dateNow)
|
||||||
_passes.postValue(Result.Success(filteredPasses))
|
_passes.value = Result.Success(filteredPasses)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateEntriesFromFile(uri: Uri) {
|
fun updateEntriesFromFile(uri: Uri) {
|
||||||
postAppEvent(Event(0))
|
_satData.value = Result.InProgress
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
satelliteRepo.updateEntriesFromFile(uri)
|
satelliteRepo.updateEntriesFromFile(uri)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
postAppEvent(Event(1))
|
_satData.value = Result.Error(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateEntriesFromSources(sources: List<TleSource>) {
|
fun updateEntriesFromSources(sources: List<TleSource> = prefsRepo.loadTleSources()) {
|
||||||
postAppEvent(Event(0))
|
_satData.value = Result.InProgress
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
prefsRepo.saveTleSources(sources)
|
prefsRepo.saveTleSources(sources)
|
||||||
satelliteRepo.updateEntriesFromWeb(sources)
|
satelliteRepo.updateEntriesFromWeb(sources)
|
||||||
satelliteRepo.updateTransmitters()
|
satelliteRepo.updateTransmitters()
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
postAppEvent(Event(1))
|
_satData.value = Result.Error(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,10 +112,6 @@ class SharedViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postAppEvent(event: Event<Int>) {
|
|
||||||
_appEvent.value = event
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPasses(satellite: Satellite, dateNow: Date): MutableList<SatPass> {
|
private fun getPasses(satellite: Satellite, dateNow: Date): MutableList<SatPass> {
|
||||||
val predictor = satellite.getPredictor(prefsRepo.getStationPosition())
|
val predictor = satellite.getPredictor(prefsRepo.getStationPosition())
|
||||||
val passes = predictor.getPasses(dateNow, prefsRepo.getHoursAhead(), true)
|
val passes = predictor.getPasses(dateNow, prefsRepo.getHoursAhead(), true)
|
||||||
@@ -130,8 +131,19 @@ class SharedViewModel @Inject constructor(
|
|||||||
passes.sortBy { it.pass.startTime }
|
passes.sortBy { it.pass.startTime }
|
||||||
return passes
|
return passes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadDataFromDb() {
|
||||||
|
_satData.value = Result.InProgress
|
||||||
|
viewModelScope.launch {
|
||||||
|
satelliteRepo.getSatItems().collect { satItems: List<SatItem> ->
|
||||||
|
if (satItems.isNotEmpty()) {
|
||||||
|
_satData.value = Result.Success(satItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateDefaultSourcesAndEntries() {
|
private fun updateDefaultSourcesAndEntries() {
|
||||||
updateEntriesFromSources(prefsRepo.loadTleSources())
|
updateEntriesFromSources()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,7 @@ import androidx.navigation.fragment.findNavController
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
|
import com.rtbishop.look4sat.data.Result
|
||||||
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
|
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
|
||||||
import com.rtbishop.look4sat.ui.SharedViewModel
|
import com.rtbishop.look4sat.ui.SharedViewModel
|
||||||
import com.rtbishop.look4sat.utility.RecyclerDivider
|
import com.rtbishop.look4sat.utility.RecyclerDivider
|
||||||
@@ -70,19 +71,16 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
setLoading()
|
viewModel.satData.observe(viewLifecycleOwner, { result ->
|
||||||
viewModel.getSatItems().observe(viewLifecycleOwner, { satItems ->
|
when (result) {
|
||||||
if (satItems.isNullOrEmpty()) {
|
is Result.Success -> {
|
||||||
setError()
|
entriesAdapter?.setItems(result.data)
|
||||||
} else {
|
setLoaded()
|
||||||
entriesAdapter?.setItems(satItems)
|
}
|
||||||
setLoaded()
|
is Result.InProgress -> {
|
||||||
}
|
setLoading()
|
||||||
})
|
}
|
||||||
viewModel.getAppEvent().observe(viewLifecycleOwner, { event ->
|
is Result.Error -> {
|
||||||
event.getContentIfNotHandled()?.let { content ->
|
|
||||||
if (content == 0) setLoading()
|
|
||||||
else if (content == 1) {
|
|
||||||
val errorMsg = getString(R.string.entries_update_error)
|
val errorMsg = getString(R.string.entries_update_error)
|
||||||
Snackbar.make(requireView(), errorMsg, Snackbar.LENGTH_SHORT).show()
|
Snackbar.make(requireView(), errorMsg, Snackbar.LENGTH_SHORT).show()
|
||||||
entriesAdapter?.let { adapter ->
|
entriesAdapter?.let { adapter ->
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
|
|||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
mapViewModel.getGSP().observe(viewLifecycleOwner, { setupPosOverlay(it) })
|
mapViewModel.getGSP().observe(viewLifecycleOwner, { setupPosOverlay(it) })
|
||||||
sharedViewModel.getPasses().observe(viewLifecycleOwner, {
|
sharedViewModel.passes.observe(viewLifecycleOwner, {
|
||||||
if (it is Result.Success && it.data.isNotEmpty()) {
|
if (it is Result.Success && it.data.isNotEmpty()) {
|
||||||
mapViewModel.setPasses(it.data)
|
mapViewModel.setPasses(it.data)
|
||||||
binding.fabPrev.setOnClickListener { mapViewModel.scrollSelection(true) }
|
binding.fabPrev.setOnClickListener { mapViewModel.scrollSelection(true) }
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
viewModel.getPasses().observe(viewLifecycleOwner, { result ->
|
viewModel.passes.observe(viewLifecycleOwner, { result ->
|
||||||
if (result is Result.Success) {
|
if (result is Result.Success) {
|
||||||
if (result.data.isEmpty()) {
|
if (result.data.isEmpty()) {
|
||||||
binding?.apply {
|
binding?.apply {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observePasses() {
|
private fun observePasses() {
|
||||||
viewModel.getPasses().observe(viewLifecycleOwner, { result ->
|
viewModel.passes.observe(viewLifecycleOwner, { result ->
|
||||||
if (result is Result.Success) {
|
if (result is Result.Success) {
|
||||||
satPass = result.data[requireArguments().getInt("index")]
|
satPass = result.data[requireArguments().getInt("index")]
|
||||||
polarView = PolarView(requireContext()).apply { setPass(satPass) }
|
polarView = PolarView(requireContext()).apply { setPass(satPass) }
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
material_version = '1.3.0'
|
material_version = '1.3.0'
|
||||||
constraint_layout_version = '2.0.4'
|
constraint_layout_version = '2.0.4'
|
||||||
lifecycle_version = '2.3.0'
|
lifecycle_version = '2.3.0'
|
||||||
navigation_version = '2.3.3'
|
navigation_version = '2.3.4'
|
||||||
preference_version = '1.1.1'
|
preference_version = '1.1.1'
|
||||||
room_version = '2.2.6'
|
room_version = '2.2.6'
|
||||||
hilt_version = '2.33-beta'
|
hilt_version = '2.33-beta'
|
||||||
|
|||||||
Reference in new issue
Block a user