diff --git a/app/src/main/java/com/rtbishop/look4sat/data/model/SatPass.kt b/app/src/main/java/com/rtbishop/look4sat/data/model/SatPass.kt index a79d0214..59dfd83b 100644 --- a/app/src/main/java/com/rtbishop/look4sat/data/model/SatPass.kt +++ b/app/src/main/java/com/rtbishop/look4sat/data/model/SatPass.kt @@ -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 } diff --git a/app/src/main/java/com/rtbishop/look4sat/data/repository/PassesRepo.kt b/app/src/main/java/com/rtbishop/look4sat/data/repository/PassesRepo.kt index 47f4d440..3e207d5d 100644 --- a/app/src/main/java/com/rtbishop/look4sat/data/repository/PassesRepo.kt +++ b/app/src/main/java/com/rtbishop/look4sat/data/repository/PassesRepo.kt @@ -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>() - val passes: SharedFlow> = _passes + private val _passes = MutableSharedFlow>>(replay = 1) + private var selectedSatellites = emptyList() + val passes: SharedFlow>> = _passes - init { - externalScope.launch { calculatePasses() } + suspend fun triggerCalculation(satellites: List, 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, refDate: Date = Date()) { + _passes.emit(Result.InProgress) withContext(defaultDispatcher) { val allPasses = mutableListOf() - 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))) } } diff --git a/app/src/main/java/com/rtbishop/look4sat/data/repository/SatelliteRepo.kt b/app/src/main/java/com/rtbishop/look4sat/data/repository/SatelliteRepo.kt index 6c344b1d..c5998e72 100644 --- a/app/src/main/java/com/rtbishop/look4sat/data/repository/SatelliteRepo.kt +++ b/app/src/main/java/com/rtbishop/look4sat/data/repository/SatelliteRepo.kt @@ -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) { @@ -94,14 +92,13 @@ class SatelliteRepo @Inject constructor( private suspend fun getStreamsForSources(sources: List): List { val streams = mutableListOf() 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 } diff --git a/app/src/main/java/com/rtbishop/look4sat/di/CoroutinesModule.kt b/app/src/main/java/com/rtbishop/look4sat/di/CoroutinesModule.kt index 1688cac0..a09e1741 100644 --- a/app/src/main/java/com/rtbishop/look4sat/di/CoroutinesModule.kt +++ b/app/src/main/java/com/rtbishop/look4sat/di/CoroutinesModule.kt @@ -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 diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesAdapter.kt b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesAdapter.kt index fbb34b77..8e752802 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesAdapter.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesAdapter.kt @@ -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() { @@ -33,35 +33,35 @@ class EntriesAdapter : RecyclerView.Adapter() { } 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, isSelected: Boolean) } fun setEntriesClickListener(listener: EntriesClickListener) { - entriesClickListener = listener + clickListener = listener } - fun submitItems(items: List) { - listDiffer.submitList(items) + fun submitList(items: List) { + 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() { 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)) } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt index cfbeec91..30911003 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt @@ -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) diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt index e151999f..6ad30638 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt @@ -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() 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 { diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/infoScreen/InfoFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/infoScreen/InfoFragment.kt index 1ed42c3e..ea8b774b 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/infoScreen/InfoFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/infoScreen/InfoFragment.kt @@ -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?) { diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapFragment.kt index 64fe2a3a..70f118d8 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapFragment.kt @@ -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) { diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapViewModel.kt index 7e6b9a79..f9878e43 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/mapScreen/MapViewModel.kt @@ -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") + } } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesAdapter.kt b/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesAdapter.kt index 01b00518..411c6d7d 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesAdapter.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesAdapter.kt @@ -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() { - private val diffCallback = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: SatPass, newItem: SatPass): Boolean { - return oldItem.catNum == newItem.catNum && oldItem.startDate == newItem.startDate - } + private var satPassList: MutableList = mutableListOf() - override fun areContentsTheSame(oldItem: SatPass, newItem: SatPass): Boolean { - return false + fun setList(list: MutableList) { + 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) { - 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) } } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesFragment.kt index 55880deb..7aa5b945 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesFragment.kt @@ -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() + private var passes = mutableListOf() 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>, - 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) - } - } } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesViewModel.kt index 21a59179..21f0d82b 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/passesScreen/PassesViewModel.kt @@ -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.InProgress) - private var passesCache = emptyList() - 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) = 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) - } - } - } } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt index 545a2cf3..97374884 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt @@ -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() { diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarViewModel.kt index 0b36f6c0..c5dff676 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarViewModel.kt @@ -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 { diff --git a/app/src/main/res/layout/item_sat_entry.xml b/app/src/main/res/layout/sat_item.xml similarity index 100% rename from app/src/main/res/layout/item_sat_entry.xml rename to app/src/main/res/layout/sat_item.xml