diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt index 789583ff..3f5c0b9d 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt @@ -17,8 +17,10 @@ */ package com.rtbishop.look4sat.presentation.passesScreen +import android.animation.ValueAnimator import android.os.Bundle import android.view.View +import android.view.animation.LinearInterpolator import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController @@ -38,8 +40,9 @@ import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener { + private val viewModel: PassesViewModel by viewModels() private lateinit var binding: FragmentPassesBinding - private val passesViewModel: PassesViewModel by viewModels() + private lateinit var refreshAnimator: ValueAnimator override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -49,7 +52,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC private fun setupComponents() { val context = requireContext() - val adapter = PassesAdapter(passesViewModel.shouldUseUTC(), this) + val adapter = PassesAdapter(viewModel.shouldUseUTC(), this) val layoutManager = LinearLayoutManager(context) val itemDecoration = DividerItemDecoration(context, layoutManager.orientation) binding.run { @@ -72,18 +75,34 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC passesFab.setOnClickListener { navigateToEntries() } passesSettingsBtn.setOnClickListener { navigateToSettings() } } - passesViewModel.passes.observe(viewLifecycleOwner) { passesResult -> + setupObservers(adapter) + setupAnimator() + } + + private fun setupObservers(adapter: PassesAdapter) { + viewModel.passes.observe(viewLifecycleOwner) { passesResult -> handleNewPasses(passesResult, adapter) } - passesViewModel.entries.observe(viewLifecycleOwner) { number -> + viewModel.entries.observe(viewLifecycleOwner) { number -> handleEntriesNumber(number) } getNavResult>(R.id.nav_passes, "prefs") { prefs -> - passesViewModel.saveCalculationPrefs(prefs.first, prefs.second) - passesViewModel.forceCalculation(prefs.first, prefs.second) + viewModel.forceCalculation(prefs.first, prefs.second) } getNavResult>(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) { is DataState.Success -> { adapter.submitList(state.data) - binding.apply { - passesRecycler.visibility = View.VISIBLE - passesErrorMsg.visibility = View.INVISIBLE + tickMainTimer(state.data) + if (state.data.isNotEmpty()) { // show new passes list + 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 -> { - binding.apply { + binding.run { + refreshAnimator.start() + passesRefreshBtn.isEnabled = false passesTimer.text = 0L.toTimerString() - passesRecycler.visibility = View.INVISIBLE passesErrorMsg.visibility = View.INVISIBLE } } - else -> { - binding.apply { - passesTimer.text = 0L.toTimerString() - passesRecycler.visibility = View.INVISIBLE - passesErrorMsg.visibility = View.VISIBLE - } - } + else -> {} } } - private fun tickMainTimer(passes: List, binding: FragmentPassesBinding) { + private fun tickMainTimer(passes: List) { if (passes.isNotEmpty()) { val timeNow = System.currentTimeMillis() try { @@ -139,7 +164,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC } private fun refreshPasses() { - passesViewModel.forceCalculation() + viewModel.forceCalculation() } private fun navigateToMap() { diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt index 72d28df2..e4edd378 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt @@ -35,7 +35,7 @@ class PassesViewModel @Inject constructor( ) : ViewModel() { private var passesProcessing: Job? = null - private val _passes = MutableLiveData>>() + private val _passes = MutableLiveData>>(DataState.Loading) val passes: LiveData>> = _passes val entries: LiveData = repository.getEntriesNumber().asLiveData() @@ -55,6 +55,8 @@ class PassesViewModel @Inject constructor( } } + fun shouldUseUTC() = settings.getUseUTC() + fun saveSelectionAndRecalc(selection: List) { viewModelScope.launch { repository.setEntriesSelection(selection) @@ -70,18 +72,11 @@ class PassesViewModel @Inject constructor( viewModelScope.launch { _passes.postValue(DataState.Loading) passesProcessing?.cancelAndJoin() + settings.setHoursAhead(hoursAhead) + settings.setMinElevation(minElevation) val satellites = repository.getSelectedEntries() val stationPos = settings.loadStationPosition() 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) - } } diff --git a/app/src/main/res/layout/fragment_passes.xml b/app/src/main/res/layout/fragment_passes.xml index 5bd5c5c8..1ef0a825 100644 --- a/app/src/main/res/layout/fragment_passes.xml +++ b/app/src/main/res/layout/fragment_passes.xml @@ -44,19 +44,13 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - - + android:textSize="@dimen/text_size_large" /> diff --git a/core/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt b/core/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt index 6e6c3621..b9b4d0e3 100644 --- a/core/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt +++ b/core/src/main/java/com/rtbishop/look4sat/domain/predict/Predictor.kt @@ -62,8 +62,7 @@ class Predictor(private val predictorDispatcher: CoroutineDispatcher) { suspend fun processPasses(passList: List, time: Long): List { return withContext(predictorDispatcher) { - val copiedList = passList.map { it.copy() } - copiedList.forEach { pass -> + passList.forEach { pass -> if (!pass.isDeepSpace) { val timeStart = pass.aosTime 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() } } }