Fixed behaviour of passes recycler ticking and refreshing

This commit is contained in:
Arty Bishop committed 2021-04-03 13:47:02 +01:00
1 parent cfd1a2f2b8
commit c2216e4917
9 files changed
+84 -71

No files matched your search

@@ -35,10 +35,10 @@ data class SatPass(
val predictor: PassPredictor,
var progress: Int = 0
) {
val startDate: Date
val aosDate: Date
get() = Date(aosTime)
val endDate: Date
val losDate: Date
get() = Date(losTime)
val tcaDate: Date
@@ -41,21 +41,28 @@ class PassesRepo @Inject constructor(
val passes: SharedFlow<Result<List<SatPass>>> = _passes
suspend fun triggerCalculation(satellites: List<Satellite>, refDate: Date = Date()) {
if (satellites.isEmpty()) _passes.emit(Result.Error(Exception()))
val oldCatNums = selectedSatellites.map { it.tle.catnum }
val newCatNums = satellites.map { it.tle.catnum }
if (oldCatNums != newCatNums) forceCalculation(satellites, refDate)
if (satellites.isEmpty()) {
_passes.emit(Result.Error(Exception()))
} else {
val oldCatNums = selectedSatellites.map { it.tle.catnum }
val newCatNums = satellites.map { it.tle.catnum }
if (oldCatNums != newCatNums) forceCalculation(satellites, refDate)
}
}
suspend fun forceCalculation(satellites: List<Satellite>, refDate: Date = Date()) {
_passes.emit(Result.InProgress)
withContext(defaultDispatcher) {
val allPasses = mutableListOf<SatPass>()
selectedSatellites = satellites
satellites.forEach { satellite ->
allPasses.addAll(getPasses(satellite, refDate))
if (satellites.isEmpty()) {
_passes.emit(Result.Error(Exception()))
} else {
_passes.emit(Result.InProgress)
withContext(defaultDispatcher) {
val allPasses = mutableListOf<SatPass>()
selectedSatellites = satellites
satellites.forEach { satellite ->
allPasses.addAll(getPasses(satellite, refDate))
}
_passes.emit(Result.Success(filterPasses(allPasses, refDate)))
}
_passes.emit(Result.Success(filterPasses(allPasses, refDate)))
}
}
@@ -66,9 +73,9 @@ class PassesRepo @Inject constructor(
private fun filterPasses(passes: List<SatPass>, refDate: Date): List<SatPass> {
val timeFuture = Date(refDate.time + (prefsManager.getHoursAhead() * 3600 * 1000))
return passes.filter { it.endDate.after(refDate) }
.filter { it.startDate.before(timeFuture) }
return passes.filter { it.losDate.after(refDate) }
.filter { it.aosDate.before(timeFuture) }
.filter { it.maxElevation > prefsManager.getMinElevation() }
.sortedBy { it.startDate }
.sortedBy { it.aosDate }
}
}
@@ -19,8 +19,6 @@ package com.rtbishop.look4sat.ui.passesScreen
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.navigation.findNavController
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
@@ -28,15 +26,15 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.databinding.ItemPassGeoBinding
import com.rtbishop.look4sat.databinding.ItemPassLeoBinding
import com.rtbishop.look4sat.utility.navigateSafe
import java.text.SimpleDateFormat
import java.util.*
class PassesAdapter(private val isUTC: Boolean) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class PassesAdapter(private val isUTC: Boolean, private val clickListener: PassesClickListener) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val diffCallback = object : DiffUtil.ItemCallback<SatPass>() {
override fun areItemsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
return oldItem.catNum == newItem.catNum && oldItem.startDate == newItem.startDate
return oldItem.catNum == newItem.catNum && oldItem.aosDate == newItem.aosDate
}
override fun areContentsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
@@ -44,30 +42,30 @@ class PassesAdapter(private val isUTC: Boolean) : RecyclerView.Adapter<RecyclerV
}
}
private val differ = AsyncListDiffer(this, diffCallback)
private var allPasses = emptyList<SatPass>()
private var currentPasses = emptyList<SatPass>()
interface PassesClickListener {
fun navigateToPass(satPass: SatPass)
}
fun submitList(passes: List<SatPass>) {
allPasses = passes.map { it.copy() }
currentPasses = passes.map { it.copy() }
currentPasses = passes
tickDiffer(System.currentTimeMillis())
}
fun tickDiffer(timeNow: Long) {
val list = currentPasses.map { it.copy() }
list.forEach { pass ->
val copiedPasses = currentPasses.map { pass -> pass.copy() }
copiedPasses.forEach { pass ->
if (!pass.isDeepSpace) {
val timeStart = pass.startDate.time
val timeStart = pass.aosDate.time
if (timeNow > timeStart) {
val timeEnd = pass.endDate.time
val deltaNow = timeNow.minus(timeStart).toFloat()
val deltaTotal = timeEnd.minus(timeStart).toFloat()
val deltaTotal = pass.losDate.time.minus(timeStart).toFloat()
pass.progress = ((deltaNow / deltaTotal) * 100).toInt()
}
}
}
currentPasses = list.filter { pass -> pass.progress < 100 }
differ.submitList(currentPasses)
differ.submitList(copiedPasses.filter { pass -> pass.progress < 100 })
}
override fun getItemCount() = differ.currentList.size
@@ -86,11 +84,10 @@ class PassesAdapter(private val isUTC: Boolean) : RecyclerView.Adapter<RecyclerV
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val pass = differ.currentList[position]
if (holder.itemViewType == 0) {
(holder as SatPassLeoHolder).bind(pass, differ.currentList.indexOf(pass), isUTC)
(holder as SatPassLeoHolder).bind(differ.currentList[position], clickListener, isUTC)
} else {
(holder as SatPassGeoHolder).bind(pass, differ.currentList.indexOf(pass))
(holder as SatPassGeoHolder).bind(differ.currentList[position], clickListener)
}
}
@@ -108,7 +105,7 @@ class PassesAdapter(private val isUTC: Boolean) : RecyclerView.Adapter<RecyclerV
private val startFormat = SimpleDateFormat(startTimeFormat, Locale.getDefault())
private val endFormat = SimpleDateFormat(endTimeFormat, Locale.getDefault())
fun bind(satPass: SatPass, passIndex: Int, shouldUseUTC: Boolean) {
fun bind(satPass: SatPass, listener: PassesClickListener, shouldUseUTC: Boolean) {
binding.apply {
if (shouldUseUTC) {
startFormat.timeZone = timeZoneUTC
@@ -119,17 +116,14 @@ class PassesAdapter(private val isUTC: Boolean) : RecyclerView.Adapter<RecyclerV
passLeoAosAz.text = String.format(aosAzFormat, satPass.aosAzimuth)
passLeoMaxEl.text = String.format(maxElFormat, satPass.maxElevation)
passLeoLosAz.text = String.format(losAzFormat, satPass.losAzimuth)
passLeoStart.text = startFormat.format(satPass.startDate)
passLeoStart.text = startFormat.format(satPass.aosDate)
passLeoAlt.text = String.format(altFormat, satPass.altitude)
passLeoEnd.text = endFormat.format(satPass.endDate)
passLeoEnd.text = endFormat.format(satPass.losDate)
passLeoProgress.progress = satPass.progress
}
itemView.setOnClickListener {
if (satPass.progress < 100) {
val bundle = bundleOf("index" to passIndex)
itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
}
listener.navigateToPass(satPass)
}
}
@@ -149,7 +143,7 @@ class PassesAdapter(private val isUTC: Boolean) : RecyclerView.Adapter<RecyclerV
private val altFormat = itemView.context.getString(R.string.pat_altitude)
private val elevFormat = itemView.context.getString(R.string.pat_elevation)
fun bind(satPass: SatPass, passIndex: Int) {
fun bind(satPass: SatPass, listener: PassesClickListener) {
binding.apply {
passGeoSatName.text = satPass.name
passGeoSatId.text = String.format(satIdFormat, satPass.catNum)
@@ -159,8 +153,7 @@ class PassesAdapter(private val isUTC: Boolean) : RecyclerView.Adapter<RecyclerV
}
itemView.setOnClickListener {
val bundle = bundleOf("index" to passIndex)
itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
listener.navigateToPass(satPass)
}
}
@@ -19,8 +19,10 @@ package com.rtbishop.look4sat.ui.passesScreen
import android.os.Bundle
import android.view.View
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R
@@ -29,14 +31,15 @@ import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.databinding.FragmentPassesBinding
import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.formatForTimer
import com.rtbishop.look4sat.utility.navigateSafe
import dagger.hilt.android.AndroidEntryPoint
import java.util.*
@AndroidEntryPoint
class PassesFragment : Fragment(R.layout.fragment_passes) {
class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener {
private val viewModel: PassesViewModel by viewModels()
private var passes = mutableListOf<SatPass>()
private var passes = listOf<SatPass>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -44,20 +47,20 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
}
private fun setupComponents(view: View) {
val passesAdapter = PassesAdapter(viewModel.shouldUseUTC())
val passesAdapter = PassesAdapter(viewModel.shouldUseUTC(), this)
val binding = FragmentPassesBinding.bind(view).apply {
passesRecycler.apply {
setHasFixedSize(true)
adapter = passesAdapter
isVerticalScrollBarEnabled = false
layoutManager = LinearLayoutManager(context)
// (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
addItemDecoration(RecyclerDivider(R.drawable.rec_divider_dark))
}
passesRefresh.setOnClickListener { viewModel.forceCalculation() }
}
viewModel.passes.observe(viewLifecycleOwner, { result ->
handleNewPasses(result, passesAdapter, binding)
viewModel.passes.observe(viewLifecycleOwner, { passesResult ->
handleNewPasses(passesResult, passesAdapter, binding)
})
viewModel.getAppTimer().observe(viewLifecycleOwner, { timeNow ->
tickMainTimer(timeNow, passesAdapter, binding)
@@ -71,7 +74,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
) {
when (result) {
is Result.Success -> {
passes = result.data.toMutableList()
passes = result.data
passesAdapter.submitList(passes)
binding.apply {
passesError.visibility = View.INVISIBLE
@@ -80,7 +83,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
}
}
is Result.InProgress -> {
passes.clear()
passes = emptyList()
binding.apply {
passesTimer.text = 0L.formatForTimer()
passesError.visibility = View.INVISIBLE
@@ -89,7 +92,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
}
}
is Result.Error -> {
passes.clear()
passes = emptyList()
binding.apply {
passesTimer.text = 0L.formatForTimer()
passesProgress.visibility = View.INVISIBLE
@@ -107,18 +110,24 @@ class PassesFragment : Fragment(R.layout.fragment_passes) {
) {
if (passes.isNotEmpty()) {
try {
val nextPass = passes.first { it.startDate.time.minus(timeNow) > 0 }
val millisBeforeStart = nextPass.startDate.time.minus(timeNow)
val nextPass = passes.first { it.aosDate.time.minus(timeNow) > 0 }
val millisBeforeStart = nextPass.aosDate.time.minus(timeNow)
binding.passesTimer.text = millisBeforeStart.formatForTimer()
} catch (e: NoSuchElementException) {
val lastPass = passes.last()
val millisBeforeEnd = lastPass.endDate.time.minus(timeNow)
val millisBeforeEnd = lastPass.losDate.time.minus(timeNow)
binding.passesTimer.text = millisBeforeEnd.formatForTimer()
}
// passesAdapter.tickPasses(timeNow)
passesAdapter.tickDiffer(timeNow)
} else {
binding.passesTimer.text = 0L.formatForTimer()
}
}
override fun navigateToPass(satPass: SatPass) {
if (satPass.progress < 100) {
val bundle = bundleOf("catNum" to satPass.catNum, "aosTime" to satPass.aosDate.time)
findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
}
}
}
@@ -117,8 +117,9 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
}
private fun observePass() {
val passId = requireArguments().getInt("index")
viewModel.getPass(passId).observe(viewLifecycleOwner) { pass ->
val catNum = requireArguments().getInt("catNum")
val aosTime = requireArguments().getLong("aosTime")
viewModel.getPass(catNum, aosTime).observe(viewLifecycleOwner) { pass ->
satPass = pass
polarView = PolarView(requireContext()).apply { setPass(pass) }
binding.frame.addView(polarView)
@@ -171,13 +172,13 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
binding.altitude.text = String.format(polarAlt, satPos.altitude)
if (!satPass.isDeepSpace) {
if (dateNow.before(satPass.startDate)) {
val millisBeforeStart = satPass.startDate.time.minus(timeNow)
if (dateNow.before(satPass.aosDate)) {
val millisBeforeStart = satPass.aosDate.time.minus(timeNow)
binding.polarTimer.text = millisBeforeStart.formatForTimer()
} else {
val millisBeforeEnd = satPass.endDate.time.minus(timeNow)
val millisBeforeEnd = satPass.losDate.time.minus(timeNow)
binding.polarTimer.text = millisBeforeEnd.formatForTimer()
if (dateNow.after(satPass.endDate)) {
if (dateNow.after(satPass.losDate)) {
binding.polarTimer.text = 0L.formatForTimer()
findNavController().popBackStack()
}
@@ -92,13 +92,13 @@ class PolarView(context: Context) : View(context) {
}
private fun drawPassTrajectory(cvs: Canvas, satPass: SatPass) {
val startTime = satPass.startDate
val endTime = satPass.endDate
val startTime = satPass.aosDate
val endTime = satPass.losDate
while (startTime.before(endTime)) {
val satPos = satPass.predictor.getSatPos(startTime)
val x = sph2CartX(satPos.azimuth, satPos.elevation, radius.toDouble())
val y = sph2CartY(satPos.azimuth, satPos.elevation, radius.toDouble())
if (startTime.compareTo(satPass.startDate) == 0) {
if (startTime.compareTo(satPass.aosDate) == 0) {
path.moveTo(x, -y)
} else {
path.lineTo(x, -y)
@@ -44,9 +44,12 @@ class PolarViewModel @Inject constructor(
}
}
fun getPass(passId: Int) = liveData {
fun getPass(catNum: Int, aosTime: Long) = liveData {
passesRepo.passes.collect { passes ->
if (passes is Result.Success) emit(passes.data[passId])
if (passes is Result.Success) {
val pass = passes.data.find { it.catNum == catNum && it.aosDate.time == aosTime }
pass?.let { emit(it) }
}
}
}
@@ -69,10 +69,10 @@ class PassPredictor(private val satellite: Satellite, private val qth: GroundSta
do {
if (count > 0) shouldWindBack = false
val pass = nextNearEarthPass(startDate, shouldWindBack)
lastAosDate = pass.startDate
lastAosDate = pass.aosDate
passes.add(pass)
startDate =
Date(pass.endDate.time + (oneQuarterOrbitMin * 3) * 60L * 1000L)
Date(pass.losDate.time + (oneQuarterOrbitMin * 3) * 60L * 1000L)
count++
} while (lastAosDate < endDate)
}
+1 -1
View File
@@ -4,7 +4,7 @@ buildscript {
gradle_plugin_version = '1.4.32'
material_version = '1.3.0'
constraint_layout_version = '2.0.4'
lifecycle_version = '2.3.0'
lifecycle_version = '2.3.1'
navigation_version = '2.3.4'
preference_version = '1.1.1'
room_version = '2.2.6'