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