Temporarily reverted Passes AsyncDiffer

This commit is contained in:
Arty Bishop committed 2021-04-02 21:01:53 +01:00
1 parent 7e97cbea1b
commit af49acf4d9
16 files changed
+157 -203

No files matched your search

@@ -32,7 +32,8 @@ data class SatPass(
val tcaAzimuth: Double,
val altitude: Double,
val maxElevation: Double,
val predictor: PassPredictor
val predictor: PassPredictor,
var progress: Int = 0
) {
val startDate: Date
get() = Date(aosTime)
@@ -42,6 +43,4 @@ data class SatPass(
val tcaDate: Date
get() = Date(tcaTime)
var progress: Int = 0
}
@@ -18,16 +18,14 @@
package com.rtbishop.look4sat.data.repository
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.di.DefaultDispatcher
import com.rtbishop.look4sat.di.ExternalScope
import com.rtbishop.look4sat.utility.PrefsManager
import com.rtbishop.look4sat.utility.getPredictor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import javax.inject.Inject
@@ -36,24 +34,28 @@ import javax.inject.Singleton
@Singleton
class PassesRepo @Inject constructor(
private val prefsManager: PrefsManager,
private val satelliteRepo: SatelliteRepo,
@ExternalScope private val externalScope: CoroutineScope,
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher
) {
private val _passes = MutableSharedFlow<List<SatPass>>()
val passes: SharedFlow<List<SatPass>> = _passes
private val _passes = MutableSharedFlow<Result<List<SatPass>>>(replay = 1)
private var selectedSatellites = emptyList<Satellite>()
val passes: SharedFlow<Result<List<SatPass>>> = _passes
init {
externalScope.launch { calculatePasses() }
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)
}
suspend fun calculatePasses(refDate: Date = Date()) {
suspend fun forceCalculation(satellites: List<Satellite>, refDate: Date = Date()) {
_passes.emit(Result.InProgress)
withContext(defaultDispatcher) {
val allPasses = mutableListOf<SatPass>()
satelliteRepo.getSelectedSatellites().forEach { satellite ->
selectedSatellites = satellites
satellites.forEach { satellite ->
allPasses.addAll(getPasses(satellite, refDate))
}
_passes.emit(filterPasses(allPasses, refDate))
_passes.emit(Result.Success(filterPasses(allPasses, refDate)))
}
}
@@ -64,9 +64,7 @@ class SatelliteRepo @Inject constructor(
val entries = importEntriesFromStreams(listOf(stream))
insertEntriesAndRestoreSelection(entries)
}
}.onFailure { throwable: Throwable ->
throw throwable
}
}.onFailure { throw it }
}
suspend fun importSatDataFromWeb(sources: List<TleSource>) {
@@ -94,14 +92,13 @@ class SatelliteRepo @Inject constructor(
private suspend fun getStreamsForSources(sources: List<TleSource>): List<InputStream> {
val streams = mutableListOf<InputStream>()
sources.forEach { tleSource ->
satelliteService.fetchFileByUrl(tleSource.url).body()?.byteStream()
?.let { inputStream ->
if (tleSource.url.contains(".zip", true)) {
streams.add(getZipStream(inputStream))
} else {
streams.add(inputStream)
}
satelliteService.fetchFileByUrl(tleSource.url).body()?.byteStream()?.let { stream ->
if (tleSource.url.contains(".zip", true)) {
streams.add(getZipStream(stream))
} else {
streams.add(stream)
}
}
}
return streams
}
@@ -22,21 +22,13 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import javax.inject.Qualifier
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object CoroutinesModule {
@Provides
@Singleton
@ExternalScope
fun providesExternalScope(): CoroutineScope = CoroutineScope(SupervisorJob())
@Provides
@DefaultDispatcher
fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
@@ -50,10 +42,6 @@ object CoroutinesModule {
fun provideMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
}
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class ExternalScope
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DefaultDispatcher
@@ -23,7 +23,7 @@ import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.databinding.ItemSatEntryBinding
import com.rtbishop.look4sat.databinding.SatItemBinding
class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
@@ -33,35 +33,35 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
}
override fun areContentsTheSame(oldItem: SatItem, newItem: SatItem): Boolean {
return oldItem.isSelected != newItem.isSelected
return oldItem.isSelected == newItem.isSelected
}
}
private val listDiffer = AsyncListDiffer(this, diffCallback)
private lateinit var entriesClickListener: EntriesClickListener
private val differ = AsyncListDiffer(this, diffCallback)
private lateinit var clickListener: EntriesClickListener
interface EntriesClickListener {
fun updateSelection(catNums: List<Int>, isSelected: Boolean)
}
fun setEntriesClickListener(listener: EntriesClickListener) {
entriesClickListener = listener
clickListener = listener
}
fun submitItems(items: List<SatItem>) {
listDiffer.submitList(items)
fun submitList(items: List<SatItem>) {
differ.submitList(items)
}
override fun getItemCount(): Int = listDiffer.currentList.size
override fun getItemCount(): Int = differ.currentList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SatItemHolder {
return SatItemHolder.from(parent)
}
override fun onBindViewHolder(holder: SatItemHolder, position: Int) {
holder.bind(listDiffer.currentList[position], entriesClickListener)
holder.bind(differ.currentList[position], clickListener)
}
class SatItemHolder private constructor(private val binding: ItemSatEntryBinding) :
class SatItemHolder private constructor(private val binding: SatItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: SatItem, listener: EntriesClickListener) {
@@ -75,7 +75,7 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
companion object {
fun from(parent: ViewGroup): SatItemHolder {
val inflater = LayoutInflater.from(parent.context)
return SatItemHolder(ItemSatEntryBinding.inflate(inflater, parent, false))
return SatItemHolder(SatItemBinding.inflate(inflater, parent, false))
}
}
}
@@ -39,10 +39,10 @@ import dagger.hilt.android.AndroidEntryPoint
class EntriesFragment : Fragment(R.layout.fragment_entries) {
private val viewModel: EntriesViewModel by viewModels()
private val filePicker =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let { viewModel.importSatDataFromFile(uri) }
}
private val contentContract = ActivityResultContracts.GetContent()
private val filePicker = registerForActivityResult(contentContract) { uri ->
uri?.let { viewModel.importSatDataFromFile(uri) }
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -86,7 +86,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
) {
when (result) {
is Result.Success -> {
entriesAdapter.submitItems(result.data)
entriesAdapter.submitList(result.data)
binding.entriesProgress.visibility = View.INVISIBLE
binding.entriesRecycler.visibility = View.VISIBLE
binding.entriesRecycler.scrollToPosition(0)
@@ -27,8 +27,8 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.data.model.TleSource
import com.rtbishop.look4sat.utility.PrefsManager
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.utility.PrefsManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@@ -86,17 +86,11 @@ class EntriesViewModel @Inject constructor(
}
fun selectCurrentItems() {
val newList = mutableListOf<SatItem>()
val currentValue = _satData.value
if (currentValue is Result.Success) {
currentValue.data.forEach { item ->
item.isSelected = shouldSelectAll
newList.add(item)
}
updateSelection(currentValue.data.map { it.catNum }, shouldSelectAll)
shouldSelectAll = shouldSelectAll.not()
}
_satData.value = Result.Success(newList)
updateSelection(newList.map { it.catNum }, shouldSelectAll)
shouldSelectAll = !shouldSelectAll
}
fun createModesDialog(context: Context): AlertDialog {
@@ -26,9 +26,7 @@ import androidx.fragment.app.Fragment
import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentInfoBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class InfoFragment : Fragment(R.layout.fragment_info) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -29,10 +29,9 @@ import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.Satellite
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.SelectedSat
import com.rtbishop.look4sat.utility.PrefsManager
import com.rtbishop.look4sat.databinding.FragmentMapBinding
import com.rtbishop.look4sat.utility.PrefsManager
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.FlowPreview
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.ITileSource
import org.osmdroid.tileprovider.tilesource.TileSourcePolicy
@@ -45,7 +44,6 @@ import timber.log.Timber
import java.util.*
import javax.inject.Inject
@FlowPreview
@AndroidEntryPoint
class MapFragment : Fragment(R.layout.fragment_map) {
@@ -34,6 +34,8 @@ import org.osmdroid.views.overlay.FolderOverlay
import org.osmdroid.views.overlay.Overlay
import org.osmdroid.views.overlay.Polygon
import org.osmdroid.views.overlay.Polyline
import timber.log.Timber
import java.lang.IllegalArgumentException
import java.util.*
import javax.inject.Inject
@@ -179,7 +181,11 @@ class MapViewModel @Inject constructor(
return Polygon().apply {
fillPaint.set(footprintPaint)
outlinePaint.set(footprintPaint)
points = rangePoints
try {
points = rangePoints
} catch (e: IllegalArgumentException) {
Timber.d("RangeCircle: ${satPos.rangeCircle}, RangePoints: $rangePoints")
}
}
}
}
@@ -19,47 +19,57 @@ package com.rtbishop.look4sat.ui.passesScreen
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.core.os.bundleOf
import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView
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 shouldUseUTC: Boolean = false) :
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
}
private var satPassList: MutableList<SatPass> = mutableListOf()
override fun areContentsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
return false
fun setList(list: MutableList<SatPass>) {
satPassList = list
notifyDataSetChanged()
}
fun tickPasses(timeNow: Long) {
val iterator = satPassList.listIterator()
while (iterator.hasNext()) {
val satPass = iterator.next()
if (!satPass.isDeepSpace) {
if (satPass.progress < 100) {
val timeStart = satPass.startDate.time
if (timeNow > timeStart) {
val timeEnd = satPass.endDate.time
val index = satPassList.indexOf(satPass)
val deltaNow = timeNow.minus(timeStart).toFloat()
val deltaTotal = timeEnd.minus(timeStart).toFloat()
satPass.progress = ((deltaNow / deltaTotal) * 100).toInt()
notifyItemChanged(index)
}
} else {
val index = satPassList.indexOf(satPass)
iterator.remove()
notifyItemRemoved(index)
}
}
}
}
private val listDiffer = AsyncListDiffer(this, diffCallback)
private lateinit var passesClickListener: PassesClickListener
interface PassesClickListener {
fun navigateToPass(satPass: SatPass)
override fun getItemCount(): Int {
return satPassList.size
}
fun setPassesClickListener(listener: PassesClickListener) {
passesClickListener = listener
}
fun submitPasses(items: List<SatPass>) {
listDiffer.submitList(items)
}
override fun getItemCount() = listDiffer.currentList.size
override fun getItemViewType(position: Int): Int {
return if (listDiffer.currentList[position].isDeepSpace) 1
return if (satPassList[position].isDeepSpace) 1
else 0
}
@@ -73,11 +83,9 @@ class PassesAdapter(private val shouldUseUTC: Boolean = false) :
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder.itemViewType == 0) {
(holder as SatPassLeoHolder)
.bind(listDiffer.currentList[position], shouldUseUTC, passesClickListener)
(holder as SatPassLeoHolder).bind(satPassList[position], position, shouldUseUTC)
} else {
(holder as SatPassGeoHolder)
.bind(listDiffer.currentList[position], passesClickListener)
(holder as SatPassGeoHolder).bind(satPassList[position], position)
}
}
@@ -95,7 +103,7 @@ class PassesAdapter(private val shouldUseUTC: Boolean = false) :
private val startFormat = SimpleDateFormat(startTimeFormat, Locale.getDefault())
private val endFormat = SimpleDateFormat(endTimeFormat, Locale.getDefault())
fun bind(satPass: SatPass, shouldUseUTC: Boolean, listener: PassesClickListener) {
fun bind(satPass: SatPass, position: Int, shouldUseUTC: Boolean) {
binding.apply {
if (shouldUseUTC) {
startFormat.timeZone = timeZoneUTC
@@ -111,8 +119,12 @@ class PassesAdapter(private val shouldUseUTC: Boolean = false) :
passLeoEnd.text = endFormat.format(satPass.endDate)
passLeoProgress.progress = satPass.progress
}
itemView.setOnClickListener {
listener.navigateToPass(satPass)
if (satPass.progress < 100) {
val bundle = bundleOf("index" to position)
itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
}
}
}
@@ -132,7 +144,7 @@ class PassesAdapter(private val shouldUseUTC: Boolean = false) :
private val altFormat = itemView.context.getString(R.string.pat_altitude)
private val elevFormat = itemView.context.getString(R.string.pat_elevation)
fun bind(satPass: SatPass, listener: PassesClickListener) {
fun bind(satPass: SatPass, position: Int) {
binding.apply {
passGeoSatName.text = satPass.name
passGeoSatId.text = String.format(satIdFormat, satPass.catNum)
@@ -140,8 +152,10 @@ class PassesAdapter(private val shouldUseUTC: Boolean = false) :
passGeoAlt.text = String.format(altFormat, satPass.altitude)
passGeoEl.text = String.format(elevFormat, satPass.maxElevation)
}
itemView.setOnClickListener {
listener.navigateToPass(satPass)
val bundle = bundleOf("index" to position)
itemView.findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
}
}
@@ -19,10 +19,8 @@ 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
@@ -31,15 +29,14 @@ 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), PassesAdapter.PassesClickListener {
class PassesFragment : Fragment(R.layout.fragment_passes) {
private val viewModel: PassesViewModel by viewModels()
private var passes = listOf<SatPass>()
private var passes = mutableListOf<SatPass>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -47,9 +44,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
}
private fun setupComponents(view: View) {
val passesAdapter = PassesAdapter(viewModel.shouldUseUTC()).apply {
setPassesClickListener(this@PassesFragment)
}
val passesAdapter = PassesAdapter(viewModel.shouldUseUTC())
val binding = FragmentPassesBinding.bind(view).apply {
passesRecycler.apply {
setHasFixedSize(true)
@@ -62,46 +57,54 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
passesRefresh.setOnClickListener { viewModel.forceCalculation() }
}
viewModel.passes.observe(viewLifecycleOwner, { result ->
handlePassesResult(result, binding, passesAdapter)
handleNewPasses(result, passesAdapter, binding)
})
viewModel.getAppTimer().observe(viewLifecycleOwner, { currentTime ->
tickMainTimer(currentTime, binding)
viewModel.getAppTimer().observe(viewLifecycleOwner, { timeNow ->
tickMainTimer(timeNow, passesAdapter, binding)
})
}
private fun handlePassesResult(
private fun handleNewPasses(
result: Result<List<SatPass>>,
binding: FragmentPassesBinding,
passesAdapter: PassesAdapter
passesAdapter: PassesAdapter,
binding: FragmentPassesBinding
) {
if (result is Result.Success) {
if (result.data.isEmpty()) {
binding.apply {
passesProgress.visibility = View.INVISIBLE
passesRecycler.visibility = View.INVISIBLE
passesError.visibility = View.VISIBLE
passesTimer.text = 0L.formatForTimer()
}
} else {
passes = result.data
passesAdapter.submitPasses(result.data)
when (result) {
is Result.Success -> {
passes = result.data.toMutableList()
passesAdapter.setList(passes)
binding.apply {
passesError.visibility = View.INVISIBLE
passesProgress.visibility = View.INVISIBLE
passesRecycler.visibility = View.VISIBLE
}
}
} else if (result is Result.InProgress) {
binding.apply {
passesTimer.text = 0L.formatForTimer()
passesError.visibility = View.INVISIBLE
passesRecycler.visibility = View.INVISIBLE
passesProgress.visibility = View.VISIBLE
is Result.InProgress -> {
passes.clear()
binding.apply {
passesTimer.text = 0L.formatForTimer()
passesError.visibility = View.INVISIBLE
passesRecycler.visibility = View.INVISIBLE
passesProgress.visibility = View.VISIBLE
}
}
is Result.Error -> {
passes.clear()
binding.apply {
passesTimer.text = 0L.formatForTimer()
passesProgress.visibility = View.INVISIBLE
passesRecycler.visibility = View.INVISIBLE
passesError.visibility = View.VISIBLE
}
}
}
}
private fun tickMainTimer(timeNow: Long, binding: FragmentPassesBinding) {
private fun tickMainTimer(
timeNow: Long,
passesAdapter: PassesAdapter,
binding: FragmentPassesBinding
) {
if (passes.isNotEmpty()) {
try {
val nextPass = passes.first { it.startDate.time.minus(timeNow) > 0 }
@@ -112,15 +115,9 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
val millisBeforeEnd = lastPass.endDate.time.minus(timeNow)
binding.passesTimer.text = millisBeforeEnd.formatForTimer()
}
passesAdapter.tickPasses(timeNow)
} else {
binding.passesTimer.text = 0L.formatForTimer()
}
}
override fun navigateToPass(satPass: SatPass) {
if (satPass.progress <= 100) {
val bundle = bundleOf("index" to passes.indexOf(satPass))
findNavController().navigateSafe(R.id.action_passes_to_polar, bundle)
}
}
}
@@ -21,29 +21,34 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.model.SatPass
import com.rtbishop.look4sat.data.repository.PassesRepo
import com.rtbishop.look4sat.di.DefaultDispatcher
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.utility.PrefsManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import timber.log.Timber
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class PassesViewModel @Inject constructor(
private val passesRepo: PassesRepo,
private val prefsManager: PrefsManager,
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher
private val satelliteRepo: SatelliteRepo,
private val passesRepo: PassesRepo
) : ViewModel() {
private val _passes = MutableStateFlow<Result<List<SatPass>>>(Result.InProgress)
private var passesCache = emptyList<SatPass>()
private var passesCalculation: Job? = null
val passes = _passes.asLiveData(viewModelScope.coroutineContext)
val passes = passesRepo.passes.asLiveData(viewModelScope.coroutineContext)
init {
viewModelScope.launch {
passesRepo.triggerCalculation(satelliteRepo.getSelectedSatellites())
}
}
fun forceCalculation() {
viewModelScope.launch {
passesRepo.forceCalculation(satelliteRepo.getSelectedSatellites())
}
}
fun getAppTimer() = liveData {
while (true) {
@@ -55,51 +60,4 @@ class PassesViewModel @Inject constructor(
fun shouldUseUTC(): Boolean {
return prefsManager.shouldUseUTC()
}
init {
viewModelScope.launch {
passesRepo.passes.collect { newPasses ->
passesCalculation?.cancelAndJoin()
passesCalculation = launch { handlePasses(newPasses) }
}
}
}
fun forceCalculation() {
viewModelScope.launch {
_passes.value = Result.InProgress
passesRepo.calculatePasses()
}
}
private suspend fun handlePasses(newPasses: List<SatPass>) = withContext(defaultDispatcher) {
if (newPasses.isEmpty()) {
_passes.value = Result.Success(newPasses)
} else {
while (isActive) {
val timeNow = System.currentTimeMillis()
passesCache = newPasses.toMutableList().apply {
val iterator = listIterator()
while (iterator.hasNext()) {
val satPass = iterator.next()
if (!satPass.isDeepSpace) {
if (satPass.progress <= 100) {
val timeStart = satPass.startDate.time
if (timeNow > timeStart) {
val deltaNow = timeNow.minus(timeStart).toFloat()
val deltaTotal = satPass.endDate.time.minus(timeStart).toFloat()
satPass.progress = ((deltaNow / deltaTotal) * 100).toInt()
}
} else {
iterator.remove()
}
}
}
}
_passes.value = Result.Success(passesCache)
Timber.d("$passesCache")
delay(1000)
}
}
}
}
@@ -36,12 +36,10 @@ import com.rtbishop.look4sat.databinding.FragmentPolarBinding
import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.formatForTimer
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.FlowPreview
import java.util.*
import kotlin.math.atan2
import kotlin.math.round
@FlowPreview
@AndroidEntryPoint
class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
@@ -120,12 +118,12 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener {
private fun observePass() {
val passId = requireArguments().getInt("index")
viewModel.getPass(passId).observe(viewLifecycleOwner, { pass ->
viewModel.getPass(passId).observe(viewLifecycleOwner) { pass ->
satPass = pass
polarView = PolarView(requireContext()).apply { setPass(pass) }
binding.frame.addView(polarView)
observeTransmitters()
})
}
}
private fun observeTransmitters() {
@@ -21,6 +21,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.data.model.Result
import com.rtbishop.look4sat.data.repository.PassesRepo
import com.rtbishop.look4sat.data.repository.SatelliteRepo
import com.rtbishop.look4sat.utility.PrefsManager
@@ -44,7 +45,11 @@ class PolarViewModel @Inject constructor(
}
fun getPass(passId: Int) = liveData {
passesRepo.passes.collect { passes -> emit(passes[passId]) }
passesRepo.passes.collect { passes ->
if (passes is Result.Success) {
emit(passes.data[passId])
}
}
}
fun shouldUseCompass(): Boolean {