Swapped SingleLiveEvent for MutableStateFlow

This commit is contained in:
Arty Bishop committed 2021-03-11 21:34:57 +00:00
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
View File
@@ -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'