Various fixes to PassesFragment's ui state handling

This commit is contained in:
Arty Bishop committed 2022-02-21 17:27:53 +00:00
1 parent 9fd0d5f2e2
commit 00549bbcba
4 files changed
+55 -42

No files matched your search

@@ -17,8 +17,10 @@
*/ */
package com.rtbishop.look4sat.presentation.passesScreen package com.rtbishop.look4sat.presentation.passesScreen
import android.animation.ValueAnimator
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.animation.LinearInterpolator
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@@ -38,8 +40,9 @@ import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint @AndroidEntryPoint
class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener { class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener {
private val viewModel: PassesViewModel by viewModels()
private lateinit var binding: FragmentPassesBinding private lateinit var binding: FragmentPassesBinding
private val passesViewModel: PassesViewModel by viewModels() private lateinit var refreshAnimator: ValueAnimator
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -49,7 +52,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
private fun setupComponents() { private fun setupComponents() {
val context = requireContext() val context = requireContext()
val adapter = PassesAdapter(passesViewModel.shouldUseUTC(), this) val adapter = PassesAdapter(viewModel.shouldUseUTC(), this)
val layoutManager = LinearLayoutManager(context) val layoutManager = LinearLayoutManager(context)
val itemDecoration = DividerItemDecoration(context, layoutManager.orientation) val itemDecoration = DividerItemDecoration(context, layoutManager.orientation)
binding.run { binding.run {
@@ -72,18 +75,34 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
passesFab.setOnClickListener { navigateToEntries() } passesFab.setOnClickListener { navigateToEntries() }
passesSettingsBtn.setOnClickListener { navigateToSettings() } passesSettingsBtn.setOnClickListener { navigateToSettings() }
} }
passesViewModel.passes.observe(viewLifecycleOwner) { passesResult -> setupObservers(adapter)
setupAnimator()
}
private fun setupObservers(adapter: PassesAdapter) {
viewModel.passes.observe(viewLifecycleOwner) { passesResult ->
handleNewPasses(passesResult, adapter) handleNewPasses(passesResult, adapter)
} }
passesViewModel.entries.observe(viewLifecycleOwner) { number -> viewModel.entries.observe(viewLifecycleOwner) { number ->
handleEntriesNumber(number) handleEntriesNumber(number)
} }
getNavResult<Pair<Int, Double>>(R.id.nav_passes, "prefs") { prefs -> getNavResult<Pair<Int, Double>>(R.id.nav_passes, "prefs") { prefs ->
passesViewModel.saveCalculationPrefs(prefs.first, prefs.second) viewModel.forceCalculation(prefs.first, prefs.second)
passesViewModel.forceCalculation(prefs.first, prefs.second)
} }
getNavResult<List<Int>>(R.id.nav_passes, "selection") { selection -> getNavResult<List<Int>>(R.id.nav_passes, "selection") { selection ->
passesViewModel.saveSelectionAndRecalc(selection) viewModel.saveSelectionAndRecalc(selection)
}
}
private fun setupAnimator() {
refreshAnimator = ValueAnimator.ofFloat(0f, -360f).apply {
duration = 875
interpolator = LinearInterpolator()
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
addUpdateListener { anim ->
binding.passesRefreshBtn.rotation = anim.animatedValue as Float
}
} }
} }
@@ -98,30 +117,36 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
when (state) { when (state) {
is DataState.Success -> { is DataState.Success -> {
adapter.submitList(state.data) adapter.submitList(state.data)
binding.apply { tickMainTimer(state.data)
passesRecycler.visibility = View.VISIBLE if (state.data.isNotEmpty()) { // show new passes list
passesErrorMsg.visibility = View.INVISIBLE binding.run {
passesRecycler.visibility = View.VISIBLE
passesErrorMsg.visibility = View.INVISIBLE
binding.passesRefreshBtn.isEnabled = true
refreshAnimator.cancel()
}
} else { // show no passes message
binding.run {
passesRecycler.visibility = View.INVISIBLE
passesErrorMsg.visibility = View.VISIBLE
binding.passesRefreshBtn.isEnabled = true
refreshAnimator.cancel()
}
} }
tickMainTimer(state.data, binding)
} }
is DataState.Loading -> { is DataState.Loading -> {
binding.apply { binding.run {
refreshAnimator.start()
passesRefreshBtn.isEnabled = false
passesTimer.text = 0L.toTimerString() passesTimer.text = 0L.toTimerString()
passesRecycler.visibility = View.INVISIBLE
passesErrorMsg.visibility = View.INVISIBLE passesErrorMsg.visibility = View.INVISIBLE
} }
} }
else -> { else -> {}
binding.apply {
passesTimer.text = 0L.toTimerString()
passesRecycler.visibility = View.INVISIBLE
passesErrorMsg.visibility = View.VISIBLE
}
}
} }
} }
private fun tickMainTimer(passes: List<SatPass>, binding: FragmentPassesBinding) { private fun tickMainTimer(passes: List<SatPass>) {
if (passes.isNotEmpty()) { if (passes.isNotEmpty()) {
val timeNow = System.currentTimeMillis() val timeNow = System.currentTimeMillis()
try { try {
@@ -139,7 +164,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
} }
private fun refreshPasses() { private fun refreshPasses() {
passesViewModel.forceCalculation() viewModel.forceCalculation()
} }
private fun navigateToMap() { private fun navigateToMap() {
@@ -35,7 +35,7 @@ class PassesViewModel @Inject constructor(
) : ViewModel() { ) : ViewModel() {
private var passesProcessing: Job? = null private var passesProcessing: Job? = null
private val _passes = MutableLiveData<DataState<List<SatPass>>>() private val _passes = MutableLiveData<DataState<List<SatPass>>>(DataState.Loading)
val passes: LiveData<DataState<List<SatPass>>> = _passes val passes: LiveData<DataState<List<SatPass>>> = _passes
val entries: LiveData<Int> = repository.getEntriesNumber().asLiveData() val entries: LiveData<Int> = repository.getEntriesNumber().asLiveData()
@@ -55,6 +55,8 @@ class PassesViewModel @Inject constructor(
} }
} }
fun shouldUseUTC() = settings.getUseUTC()
fun saveSelectionAndRecalc(selection: List<Int>) { fun saveSelectionAndRecalc(selection: List<Int>) {
viewModelScope.launch { viewModelScope.launch {
repository.setEntriesSelection(selection) repository.setEntriesSelection(selection)
@@ -70,18 +72,11 @@ class PassesViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
_passes.postValue(DataState.Loading) _passes.postValue(DataState.Loading)
passesProcessing?.cancelAndJoin() passesProcessing?.cancelAndJoin()
settings.setHoursAhead(hoursAhead)
settings.setMinElevation(minElevation)
val satellites = repository.getSelectedEntries() val satellites = repository.getSelectedEntries()
val stationPos = settings.loadStationPosition() val stationPos = settings.loadStationPosition()
predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation) predictor.forceCalculation(satellites, stationPos, timeRef, hoursAhead, minElevation)
} }
} }
fun shouldUseUTC(): Boolean {
return settings.getUseUTC()
}
fun saveCalculationPrefs(hoursAhead: Int, minElevation: Double) {
settings.setHoursAhead(hoursAhead)
settings.setMinElevation(minElevation)
}
} }
+1 -7
View File
@@ -44,19 +44,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/passes_progress"
android:visibility="invisible"
style="@style/RecyclerProgress" />
<TextView <TextView
android:id="@+id/passes_error_msg" android:id="@+id/passes_error_msg"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:text="@string/passes_error" android:text="@string/passes_error"
android:textSize="@dimen/text_size_large" android:textSize="@dimen/text_size_large" />
android:visibility="invisible" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
@@ -62,8 +62,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass> { suspend fun processPasses(passList: List<SatPass>, time: Long): List<SatPass> {
return withContext(predictorDispatcher) { return withContext(predictorDispatcher) {
val copiedList = passList.map { it.copy() } passList.forEach { pass ->
copiedList.forEach { pass ->
if (!pass.isDeepSpace) { if (!pass.isDeepSpace) {
val timeStart = pass.aosTime val timeStart = pass.aosTime
if (time > timeStart) { if (time > timeStart) {
@@ -73,7 +72,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) {
} }
} }
} }
copiedList.filter { it.progress < 100 }.map { it.copy() } passList.filter { pass -> pass.progress < 100 }.map { it.copy() }
} }
} }