Temporarily reverted Passes AsyncDiffer
This commit is contained in:
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 {
|
||||
|
||||
File renamed without changes.
Reference in new issue
Block a user