Enabled data update progress indicator

This commit is contained in:
Arty Bishop committed 2022-02-07 16:01:40 +00:00
1 parent 61cbc212b5
commit 9b38e2c63e
5 files changed
+95 -54

No files matched your search

@@ -37,6 +37,8 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module
@@ -46,7 +48,7 @@ object CoreModule {
@Provides @Provides
@Singleton @Singleton
fun provideSatelliteRepo( fun provideSatelliteRepo(
settingsHandler: ISettingsHandler, settings: ISettingsHandler,
@ApplicationContext context: Context, @ApplicationContext context: Context,
@IoDispatcher ioDispatcher: CoroutineDispatcher, @IoDispatcher ioDispatcher: CoroutineDispatcher,
@DefaultDispatcher defaultDispatcher: CoroutineDispatcher @DefaultDispatcher defaultDispatcher: CoroutineDispatcher
@@ -57,7 +59,8 @@ object CoreModule {
.fallbackToDestructiveMigration().build() .fallbackToDestructiveMigration().build()
val localSource = LocalSource(db.entriesDao(), db.transmittersDao()) val localSource = LocalSource(db.entriesDao(), db.transmittersDao())
val remoteSource = RemoteSource(ioDispatcher) val remoteSource = RemoteSource(ioDispatcher)
return DataRepository(dataParser, localSource, remoteSource, settingsHandler) val repositoryScope = CoroutineScope(SupervisorJob())
return DataRepository(dataParser, settings, localSource, remoteSource, repositoryScope)
} }
@Provides @Provides
@@ -56,7 +56,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
} }
private val contentContract = ActivityResultContracts.GetContent() private val contentContract = ActivityResultContracts.GetContent()
private val contentRequest = registerForActivityResult(contentContract) { uri -> private val contentRequest = registerForActivityResult(contentContract) { uri ->
viewModel.updateDataFromFile(uri) uri?.let { viewModel.updateDataFromFile(uri) }
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -70,7 +70,10 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
setupOtherCard(settingsBinding) setupOtherCard(settingsBinding)
setupWarrantyCard(settingsBinding) setupWarrantyCard(settingsBinding)
viewModel.stationPosition.asLiveData().observe(viewLifecycleOwner) { stationPos -> viewModel.stationPosition.asLiveData().observe(viewLifecycleOwner) { stationPos ->
stationPos?.let { handleStationPosition(it, settingsBinding) } stationPos?.let { handleStationPosition(stationPos, settingsBinding) }
}
viewModel.getUpdateState().asLiveData().observe(viewLifecycleOwner) { updateState ->
updateState?.let { handleSatState(updateState, settingsBinding) }
} }
} }
@@ -171,13 +174,13 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
is DataState.Success -> { is DataState.Success -> {
setPositionText(pos.data, binding) setPositionText(pos.data, binding)
binding.prefsLocation.locationProgress.isIndeterminate = false binding.prefsLocation.locationProgress.isIndeterminate = false
showToast(getString(R.string.pref_pos_success))
viewModel.setPositionHandled() viewModel.setPositionHandled()
showToast(getString(R.string.pref_pos_success))
} }
is DataState.Error -> { is DataState.Error -> {
binding.prefsLocation.locationProgress.isIndeterminate = false binding.prefsLocation.locationProgress.isIndeterminate = false
showToast(pos.message.toString())
viewModel.setPositionHandled() viewModel.setPositionHandled()
showToast(pos.message.toString())
} }
DataState.Loading -> { DataState.Loading -> {
binding.prefsLocation.locationProgress.isIndeterminate = true binding.prefsLocation.locationProgress.isIndeterminate = true
@@ -186,6 +189,25 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
} }
} }
private fun handleSatState(state: DataState<Long>, binding: FragmentSettingsBinding) {
when (state) {
is DataState.Success -> {
binding.prefsData.updateProgress.isIndeterminate = false
viewModel.setUpdateHandled()
showToast("Data updated successfully")
}
is DataState.Error -> {
binding.prefsData.updateProgress.isIndeterminate = false
viewModel.setUpdateHandled()
showToast(state.message.toString())
}
is DataState.Loading -> {
binding.prefsData.updateProgress.isIndeterminate = true
}
is DataState.Handled -> {}
}
}
private fun showToast(message: String) { private fun showToast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
} }
@@ -10,7 +10,6 @@ import com.rtbishop.look4sat.domain.ILocationHandler
import com.rtbishop.look4sat.domain.model.DataState import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.GeoPos import com.rtbishop.look4sat.domain.predict.GeoPos
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -21,14 +20,10 @@ class SettingsViewModel @Inject constructor(
private val settings: ISettingsHandler, private val settings: ISettingsHandler,
private val repository: IDataRepository, private val repository: IDataRepository,
private val locationHandler: ILocationHandler private val locationHandler: ILocationHandler
) : ViewModel(), ILocationHandler { ) : ViewModel() {
private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("Handled $exception in CoroutineExceptionHandler")
}
fun updateDataFromFile(uri: Uri) { fun updateDataFromFile(uri: Uri) {
viewModelScope.launch(exceptionHandler) { viewModelScope.launch {
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
resolver.openInputStream(uri)?.use { fileUri -> resolver.openInputStream(uri)?.use { fileUri ->
repository.updateDataFromFile(fileUri) repository.updateDataFromFile(fileUri)
@@ -37,10 +32,8 @@ class SettingsViewModel @Inject constructor(
} }
fun updateDataFromWeb(sources: List<String>) { fun updateDataFromWeb(sources: List<String>) {
viewModelScope.launch(exceptionHandler) {
repository.updateDataFromWeb(sources) repository.updateDataFromWeb(sources)
} }
}
fun getUseUTC(): Boolean = settings.getUseUTC() fun getUseUTC(): Boolean = settings.getUseUTC()
@@ -66,21 +59,19 @@ class SettingsViewModel @Inject constructor(
fun setRotatorPort(value: String) = settings.setRotatorPort(value) fun setRotatorPort(value: String) = settings.setRotatorPort(value)
override val stationPosition: SharedFlow<DataState<GeoPos>> = locationHandler.stationPosition fun getUpdateState() = repository.dataUpdateState
override fun getStationPosition(): GeoPos = locationHandler.getStationPosition() fun setUpdateHandled() = repository.setDataUpdateHandled()
override fun setStationPosition(latitude: Double, longitude: Double) { val stationPosition: SharedFlow<DataState<GeoPos>> = locationHandler.stationPosition
locationHandler.setStationPosition(latitude, longitude)
} fun getStationPosition(): GeoPos = locationHandler.getStationPosition()
override fun setPositionFromGps() = locationHandler.setPositionFromGps() fun setPositionFromGps() = locationHandler.setPositionFromGps()
override fun setPositionFromNet() = locationHandler.setPositionFromNet() fun setPositionFromNet() = locationHandler.setPositionFromNet()
override fun setPositionFromQth(qthString: String) { fun setPositionFromQth(qthString: String) = locationHandler.setPositionFromQth(qthString)
locationHandler.setPositionFromQth(qthString)
} fun setPositionHandled() = locationHandler.setPositionHandled()
override fun setPositionHandled() = locationHandler.setPositionHandled()
} }
@@ -19,38 +19,51 @@ package com.rtbishop.look4sat.data
import com.rtbishop.look4sat.domain.DataParser import com.rtbishop.look4sat.domain.DataParser
import com.rtbishop.look4sat.domain.IDataRepository import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatEntry import com.rtbishop.look4sat.domain.model.SatEntry
import kotlinx.coroutines.Deferred import com.rtbishop.look4sat.domain.model.SatItem
import kotlinx.coroutines.async import kotlinx.coroutines.*
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.io.InputStream import java.io.InputStream
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
class DataRepository( class DataRepository(
private val dataParser: DataParser, private val dataParser: DataParser,
private val settings: ISettingsHandler,
private val localSource: ILocalSource, private val localSource: ILocalSource,
private val remoteSource: IRemoteSource, private val remoteSource: IRemoteSource,
private val settingsHandler: ISettingsHandler private val repositoryScope: CoroutineScope
) : IDataRepository { ) : IDataRepository {
override fun getSatelliteItems() = localSource.getSatelliteItems() private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("DataRepository: handled $exception")
_updateState.value = DataState.Error(exception.message)
}
private val _updateState = MutableStateFlow<DataState<Long>>(DataState.Handled)
override val dataUpdateState: StateFlow<DataState<Long>> = _updateState
override suspend fun getSelectedSatellites() = localSource.getSelectedSatellites() override fun setDataUpdateHandled() {
_updateState.value = DataState.Handled
override suspend fun getTransmitters(catnum: Int) = localSource.getTransmitters(catnum)
override suspend fun updateDataFromFile(stream: InputStream) {
localSource.updateEntries(importSatellites(stream))
} }
override suspend fun updateDataFromWeb(sources: List<String>) { override fun updateDataFromFile(stream: InputStream) {
coroutineScope { repositoryScope.launch(exceptionHandler) {
launch { _updateState.value = DataState.Loading
settingsHandler.saveDataSources(sources) val importedSatellites = importSatellites(stream)
localSource.updateEntries(importedSatellites)
_updateState.value = DataState.Success(0L)
} }
launch { }
override fun updateDataFromWeb(sources: List<String>) {
_updateState.value = DataState.Loading
repositoryScope.launch(exceptionHandler) {
settings.saveDataSources(sources)
}
repositoryScope.launch(exceptionHandler) {
val updateTimeMillis = measureTimeMillis { val updateTimeMillis = measureTimeMillis {
val jobsMap = mutableMapOf<String, Deferred<InputStream?>>() val jobsMap = mutableMapOf<String, Deferred<InputStream?>>()
val streamsMap = mutableMapOf<String, InputStream?>() val streamsMap = mutableMapOf<String, InputStream?>()
@@ -76,15 +89,21 @@ class DataRepository(
localSource.updateEntries(entries) localSource.updateEntries(entries)
} }
println("Update from web took $updateTimeMillis ms") println("Update from web took $updateTimeMillis ms")
_updateState.value = DataState.Success(updateTimeMillis)
} }
launch { repositoryScope.launch(exceptionHandler) {
remoteSource.fetchFileStream(settingsHandler.transmittersSource)?.let { stream -> remoteSource.fetchFileStream(settings.transmittersSource)?.let { stream ->
val transmitters = dataParser.parseJSONStream(stream) val transmitters = dataParser.parseJSONStream(stream)
localSource.updateTransmitters(transmitters) localSource.updateTransmitters(transmitters)
} }
} }
} }
}
override fun getSatelliteItems(): Flow<List<SatItem>> = localSource.getSatelliteItems()
override suspend fun getSelectedSatellites() = localSource.getSelectedSatellites()
override suspend fun getTransmitters(catnum: Int) = localSource.getTransmitters(catnum)
override suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean) { override suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean) {
localSource.updateEntriesSelection(catnums, isSelected) localSource.updateEntriesSelection(catnums, isSelected)
@@ -17,23 +17,29 @@
*/ */
package com.rtbishop.look4sat.domain package com.rtbishop.look4sat.domain
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatItem import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.domain.model.Transmitter import com.rtbishop.look4sat.domain.model.Transmitter
import com.rtbishop.look4sat.domain.predict.Satellite import com.rtbishop.look4sat.domain.predict.Satellite
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import java.io.InputStream import java.io.InputStream
interface IDataRepository { interface IDataRepository {
val dataUpdateState: StateFlow<DataState<Long>>
fun setDataUpdateHandled()
fun updateDataFromFile(stream: InputStream)
fun updateDataFromWeb(sources: List<String>)
fun getSatelliteItems(): Flow<List<SatItem>> fun getSatelliteItems(): Flow<List<SatItem>>
suspend fun getSelectedSatellites(): List<Satellite> suspend fun getSelectedSatellites(): List<Satellite>
suspend fun getTransmitters(catnum: Int): List<Transmitter> suspend fun getTransmitters(catnum: Int): List<Transmitter>
suspend fun updateDataFromFile(stream: InputStream)
suspend fun updateDataFromWeb(sources: List<String>)
suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean = true) suspend fun updateSelection(catnums: List<Int>, isSelected: Boolean = true)
} }