Moved sat data update back to SharedViewModel.kt

This commit is contained in:
Arty Bishop committed 2021-03-16 20:02:28 +00:00
1 parent d013afbf50
commit c981f0900a
4 files changed
+88 -74

No files matched your search

@@ -21,36 +21,31 @@ import android.content.ContentResolver
import android.net.Uri
import com.github.amsacode.predict4java.Satellite
import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.data.*
import com.rtbishop.look4sat.di.ExternalScope
import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.data.SatTrans
import com.rtbishop.look4sat.data.TleSource
import com.rtbishop.look4sat.di.IoDispatcher
import com.rtbishop.look4sat.repository.localData.SatelliteDao
import com.rtbishop.look4sat.repository.remoteData.SatelliteService
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import timber.log.Timber
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.util.zip.ZipInputStream
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.system.measureTimeMillis
@Singleton
class SatelliteRepo @Inject constructor(
private val resolver: ContentResolver,
private val satelliteDao: SatelliteDao,
private val satelliteService: SatelliteService,
@ExternalScope private val externalScope: CoroutineScope,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) {
private val _satData = MutableStateFlow<Result<List<SatItem>>>(Result.InProgress)
val satData: Flow<Result<List<SatItem>>> = _satData
init {
loadEntriesFromDb()
}
val satDataFlow = satelliteDao.getSatItems()
fun getTransmittersForSat(catNum: Int): Flow<List<SatTrans>> {
return satelliteDao.getTransmittersForSat(catNum)
@@ -65,49 +60,32 @@ class SatelliteRepo @Inject constructor(
}
suspend fun updateSatDataFromFile(uri: Uri) {
_satData.value = Result.InProgress
externalScope.launch {
runCatching {
resolver.openInputStream(uri)?.use { stream ->
val entries = importEntriesFromStreams(listOf(stream))
insertEntriesAndRestoreSelection(entries)
}
}.onFailure { throwable: Throwable ->
Timber.d("$throwable")
_satData.value = Result.Error(throwable)
runCatching {
resolver.openInputStream(uri)?.use { stream ->
val entries = importEntriesFromStreams(listOf(stream))
insertEntriesAndRestoreSelection(entries)
}
loadEntriesFromDb()
}.onFailure { throwable: Throwable ->
throw throwable
}
}
suspend fun updateSatDataFromWeb(sources: List<TleSource>) {
_satData.value = Result.InProgress
externalScope.launch {
val updateTimeMillis = measureTimeMillis {
runCatching {
val streams = externalScope.async { getStreamsForSources(sources) }
val transmitters = externalScope.async { satelliteService.fetchTransmitters() }
val entries = importEntriesFromStreams(streams.await())
satelliteDao.insertTransmitters(transmitters.await())
insertEntriesAndRestoreSelection(entries)
}.onFailure { throwable: Throwable ->
Timber.d("$throwable")
_satData.value = Result.Error(throwable)
}
loadEntriesFromDb()
}
Timber.d("Update from Web took $updateTimeMillis ms")
coroutineScope {
launch { updateEntriesFromSources(sources) }
launch { updateTransmitters() }
}
}
private fun loadEntriesFromDb() {
_satData.value = Result.InProgress
externalScope.launch {
satelliteDao.getSatItems().collect { satItems ->
delay(250)
_satData.value = Result.Success(satItems)
}
}
private suspend fun updateEntriesFromSources(sources: List<TleSource>) {
val streams = getStreamsForSources(sources)
val entries = importEntriesFromStreams(streams)
insertEntriesAndRestoreSelection(entries)
}
private suspend fun updateTransmitters() {
val transmitters = satelliteService.fetchTransmitters()
satelliteDao.insertTransmitters(transmitters)
}
private suspend fun getStreamsForSources(sources: List<TleSource>): List<InputStream> {
@@ -115,10 +93,7 @@ class SatelliteRepo @Inject constructor(
sources.forEach { tleSource ->
satelliteService.fetchFile(tleSource.url).body()?.byteStream()?.let { inputStream ->
if (tleSource.url.contains(".zip", true)) {
// Handle zip stream
val zipInputStream = ZipInputStream(inputStream)
val zipEntry = zipInputStream.nextEntry
if (zipEntry != null && !zipEntry.isDirectory) streams.add(zipInputStream)
streams.add(getZipStream(inputStream))
} else {
streams.add(inputStream)
}
@@ -127,6 +102,17 @@ class SatelliteRepo @Inject constructor(
return streams
}
private suspend fun getZipStream(inputStream: InputStream): InputStream {
var stream: InputStream
withContext(ioDispatcher) {
ZipInputStream(inputStream).apply {
nextEntry
stream = this
}
}
return stream
}
private suspend fun importEntriesFromStreams(streams: List<InputStream>): List<SatEntry> {
val importedEntries = mutableListOf<SatEntry>()
withContext(ioDispatcher) {
@@ -31,9 +31,13 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.*
import javax.inject.Inject
import kotlin.system.measureTimeMillis
@HiltViewModel
class SharedViewModel @Inject constructor(
@@ -41,19 +45,57 @@ class SharedViewModel @Inject constructor(
private val satelliteRepo: SatelliteRepo,
) : ViewModel() {
private val _satData = MutableStateFlow<Result<List<SatItem>>>(Result.InProgress)
private val _passes = MutableStateFlow<Result<MutableList<SatPass>>>(Result.InProgress)
val passes: LiveData<Result<MutableList<SatPass>>> = _passes.asLiveData()
val satData = satelliteRepo.satData.asLiveData()
val satData = _satData.asLiveData(viewModelScope.coroutineContext)
init {
if (prefsRepo.isFirstLaunch()) {
updateDefaultSourcesAndEntries()
updateSatDataFromSources()
prefsRepo.setFirstLaunchDone()
}
loadSatData()
calculatePasses()
}
private fun loadSatData() {
viewModelScope.launch {
delay(8)
satelliteRepo.satDataFlow.map { Result.Success(it) }.collect {
_satData.value = it
}
}
}
fun updateSatDataFromFile(uri: Uri) {
_satData.value = Result.InProgress
viewModelScope.launch {
try {
satelliteRepo.updateSatDataFromFile(uri)
} catch (exception: Exception) {
_satData.value = Result.Error(exception)
}
loadSatData()
}
}
fun updateSatDataFromSources(sources: List<TleSource> = prefsRepo.loadTleSources()) {
_satData.value = Result.InProgress
viewModelScope.launch {
val updateMillis = measureTimeMillis {
try {
prefsRepo.saveTleSources(sources)
satelliteRepo.updateSatDataFromWeb(sources)
} catch (exception: Exception) {
_satData.value = Result.Error(exception)
}
loadSatData()
}
Timber.d("Update from WEB took $updateMillis ms")
}
}
fun getAppTimer() = liveData {
while (true) {
emit(System.currentTimeMillis())
@@ -76,19 +118,6 @@ class SharedViewModel @Inject constructor(
}
}
fun updateEntriesFromFile(uri: Uri) {
viewModelScope.launch {
satelliteRepo.updateSatDataFromFile(uri)
}
}
fun updateEntriesFromSources(sources: List<TleSource> = prefsRepo.loadTleSources()) {
viewModelScope.launch {
prefsRepo.saveTleSources(sources)
satelliteRepo.updateSatDataFromWeb(sources)
}
}
fun updateItemsSelection(items: List<SatItem>) {
_passes.value = Result.InProgress
viewModelScope.launch {
@@ -117,8 +146,4 @@ class SharedViewModel @Inject constructor(
passes.sortBy { it.pass.startTime }
return passes
}
private fun updateDefaultSourcesAndEntries() {
updateEntriesFromSources()
}
}
@@ -33,6 +33,7 @@ import com.rtbishop.look4sat.ui.SharedViewModel
import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.navigateSafe
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.ExperimentalCoroutinesApi
@AndroidEntryPoint
class EntriesFragment : Fragment(R.layout.fragment_entries) {
@@ -40,11 +41,12 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
private val viewModel: SharedViewModel by activityViewModels()
private val filePicker =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let { viewModel.updateEntriesFromFile(uri) }
uri?.let { viewModel.updateSatDataFromFile(uri) }
}
private var binding: FragmentEntriesBinding? = null
private var entriesAdapter: EntriesAdapter? = null
@ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentEntriesBinding.bind(view)
@@ -70,6 +72,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
}
}
@ExperimentalCoroutinesApi
private fun setupObservers() {
viewModel.satData.observe(viewLifecycleOwner, { result ->
when (result) {
@@ -55,7 +55,7 @@ class SourcesDialog : AppCompatDialogFragment() {
sourcesAdapter.addSource()
}
tleSourcesBtnPos.setOnClickListener {
viewModel.updateEntriesFromSources(sourcesAdapter.getSources())
viewModel.updateSatDataFromSources(sourcesAdapter.getSources())
dismiss()
}
tleSourcesBtnNeg.setOnClickListener { dismiss() }