mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Fixed behaviour of passes recycler ticking and refreshing
This commit is contained in:
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
@@ -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'
|
||||
|
||||
Reference in new issue
Block a user