Created SelectionRepository, cleaned up code

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:33 +00:00
1 parent 5d2a870319
commit 208c587ddf
12 files changed
+156 -130

No files matched your search

@@ -9,6 +9,7 @@ import androidx.room.Room
import com.rtbishop.look4sat.data.DataParser
import com.rtbishop.look4sat.data.DataRepository
import com.rtbishop.look4sat.data.SatelliteRepository
import com.rtbishop.look4sat.data.SelectionRepository
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteRepository
import com.rtbishop.look4sat.domain.ISensorRepository
@@ -58,6 +59,10 @@ class MainContainer(private val context: Context) {
return DataRepository(parser, fileSource, entries, radios, remoteSource, scope, settings)
}
fun provideSelectionRepository(): SelectionRepository {
return SelectionRepository(Dispatchers.Default, dataRepository, settingsRepository)
}
private fun provideSensorRepository(): ISensorRepository {
val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
@@ -28,9 +28,9 @@ interface EntriesDao {
@Query("SELECT COUNT(*) FROM entries")
fun getEntriesTotal(): Flow<Int>
@Transaction
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
suspend fun getEntriesWithModes(): List<SatItem>
// @Transaction
@Query("SELECT catnum, name FROM entries ORDER BY name ASC")
suspend fun getEntriesList(): List<SatItem>
@Transaction
@Query("SELECT * FROM entries WHERE catnum IN (:selectedIds)")
@@ -29,8 +29,8 @@ class EntriesStorage(private val entriesDao: EntriesDao) : IEntriesStorage {
override fun getEntriesTotal() = entriesDao.getEntriesTotal()
override suspend fun getEntriesWithModes(): List<DomainItem> {
return entriesDao.getEntriesWithModes().toDomainItems()
override suspend fun getEntriesList(): List<DomainItem> {
return entriesDao.getEntriesList().toDomainItems()
}
override suspend fun getEntriesWithIds(ids: List<Int>): List<Satellite> {
@@ -50,7 +50,7 @@ class EntriesStorage(private val entriesDao: EntriesDao) : IEntriesStorage {
private fun DomainEntry.toFramework() = FrameworkEntry(this.data, this.comment)
private fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, this.modes, false)
private fun FrameworkItem.toDomain() = DomainItem(this.catnum, this.name, false)
private fun List<DomainEntry>.toFrameworkEntries() = this.map { entry -> entry.toFramework() }
@@ -17,16 +17,14 @@
*/
package com.rtbishop.look4sat.framework.model
import androidx.room.Relation
data class SatItem(
val catnum: Int,
val name: String,
@Relation(
parentColumn = "catnum",
entity = SatRadio::class,
entityColumn = "catnum",
projection = ["mode"]
)
val modes: List<String>
val name: String
// @Relation(
// parentColumn = "catnum",
// entity = SatRadio::class,
// entityColumn = "catnum",
// projection = ["mode"]
// )
// val modes: List<String>
)
@@ -9,7 +9,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -26,7 +25,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.model.DataState
import com.rtbishop.look4sat.model.SatItem
import com.rtbishop.look4sat.presentation.CardIcon
import com.rtbishop.look4sat.presentation.CardLoadingIndicator
@@ -37,25 +35,33 @@ import com.rtbishop.look4sat.presentation.onClick
@Composable
fun EntriesScreen(navToPasses: () -> Unit) {
val viewModel = viewModel(EntriesViewModel::class.java, factory = EntriesViewModel.Factory)
val state = viewModel.satData.collectAsState(initial = DataState.Loading)
val uiState = viewModel.uiState.value
val showDialog = rememberSaveable { mutableStateOf(false) }
val toggleDialog = { showDialog.value = showDialog.value.not() }
if (showDialog.value) {
TypesDialog(list = viewModel.satTypes, selected = viewModel.getSatType(), toggleDialog) {
viewModel.setSatType(it)
TypesDialog(list = uiState.typesList, selected = uiState.currentType, toggleDialog) {
viewModel.setType(it)
}
}
val unselectAll = { viewModel.selectCurrentItems(false) }
val selectAll = { viewModel.selectCurrentItems(true) }
val unselectAll = { viewModel.updateSelection(false) }
val selectAll = { viewModel.updateSelection(true) }
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
TopBar(setQuery = { newQuery: String -> viewModel.setQuery(newQuery) }, saveSelection = {
viewModel.saveSelection()
navToPasses()
})
MiddleBar(viewModel.getSatType(), { toggleDialog() }, { unselectAll() }, { selectAll() })
EntriesCard(state = state.value) { list, value -> viewModel.updateSelection(list, value) }
MiddleBar(uiState.currentType, { toggleDialog() }, { unselectAll() }, { selectAll() })
ElevatedCard(modifier = Modifier.fillMaxSize()) {
if (uiState.isLoading) {
CardLoadingIndicator()
} else {
EntriesCard(uiState.itemsList) { list, value ->
viewModel.updateSelection(list, value)
}
}
}
}
}
@@ -169,7 +175,7 @@ private fun EntryTypeCard(type: String, onClick: () -> Unit, modifier: Modifier
@Preview(showBackground = true)
@Composable
private fun EntryPreview() {
val satItem = SatItem(45555, "SatName", emptyList(), true)
val satItem = SatItem(45555, "Very long Satellite name", true)
MainTheme { Entry(item = satItem, onSelected = { _, _ -> run {} }, modifier = Modifier) }
}
@@ -206,23 +212,15 @@ private fun Entry(item: SatItem, onSelected: (List<Int>, Boolean) -> Unit, modif
@Preview(showBackground = true)
@Composable
private fun EntriesPreview() {
MainTheme { EntriesCard(DataState.Loading) { _, _ -> run {} } }
MainTheme { EntriesCard(emptyList()) { _, _ -> run {} } }
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun EntriesCard(state: DataState<List<SatItem>>, onSelected: (List<Int>, Boolean) -> Unit) {
ElevatedCard(modifier = Modifier.fillMaxSize()) {
when (state) {
is DataState.Success -> {
LazyColumn {
items(items = state.data, key = { item -> item.catnum }) { entry ->
Entry(entry, onSelected, Modifier.animateItemPlacement())
}
}
}
else -> CardLoadingIndicator()
fun EntriesCard(items: List<SatItem>, onSelected: (List<Int>, Boolean) -> Unit) {
LazyColumn {
items(items = items, key = { item -> item.catnum }) { entry ->
Entry(entry, onSelected, Modifier.animateItemPlacement())
}
}
}
@@ -0,0 +1,10 @@
package com.rtbishop.look4sat.presentation.entries
import com.rtbishop.look4sat.model.SatItem
data class EntriesScreenState(
val isLoading: Boolean,
val itemsList: List<SatItem>,
val currentType: String,
val typesList: List<String>,
)
@@ -17,115 +17,59 @@
*/
package com.rtbishop.look4sat.presentation.entries
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.MainApplication
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISettingsRepository
import com.rtbishop.look4sat.model.DataState
import com.rtbishop.look4sat.model.SatItem
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import com.rtbishop.look4sat.data.SelectionRepository
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
@OptIn(ExperimentalCoroutinesApi::class)
class EntriesViewModel(
private val dataRepository: IDataRepository,
private val settingsRepository: ISettingsRepository,
private val dispatcher: CoroutineDispatcher = Dispatchers.Default
) : ViewModel() {
class EntriesViewModel(private val selectionRepo: SelectionRepository) : ViewModel() {
private val satType = MutableStateFlow("All")
private val currentQuery = MutableStateFlow(String())
private val itemsFromRepo = MutableStateFlow<List<SatItem>>(emptyList())
private val itemsWithType = satType.flatMapLatest { type ->
itemsFromRepo.map { items -> filterByType(items, type) }
}
private val itemsWithQuery = currentQuery.flatMapLatest { query ->
itemsWithType.map { items -> filterByQuery(items, query) }
}
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
val satTypes = dataRepository.getSatelliteTypes()
private val defaultUiState = EntriesScreenState(
isLoading = true,
itemsList = emptyList(),
currentType = selectionRepo.getCurrentType(),
typesList = selectionRepo.getTypesList()
)
val uiState = mutableStateOf(defaultUiState)
init {
viewModelScope.launch {
delay(250)
itemsFromRepo.value = dataRepository.getEntriesWithSelection()
delay(1000)
selectionRepo.getEntriesFlow().collect { items ->
uiState.value = uiState.value.copy(isLoading = false, itemsList = items)
}
}
}
fun getSatType() = satType.value
fun setSatType(type: String) {
satType.value = type
fun setType(type: String) = viewModelScope.launch {
selectionRepo.setType(type)
uiState.value = uiState.value.copy(currentType = type)
}
fun selectCurrentItems(selectAll: Boolean) = viewModelScope.launch {
updateEntriesSelection(itemsWithQuery.first().map { item -> item.catnum }, selectAll)
}
fun setQuery(query: String) = viewModelScope.launch { selectionRepo.setQuery(query) }
fun setQuery(query: String) {
currentQuery.value = query
}
fun saveSelection() = viewModelScope.launch {
val newSelection = itemsFromRepo.value.filter { it.isSelected }.map { it.catnum }
settingsRepository.saveEntriesSelection(newSelection)
fun updateSelection(selectAll: Boolean) = viewModelScope.launch {
selectionRepo.updateSelection(selectAll)
}
fun updateSelection(catNums: List<Int>, isSelected: Boolean) = viewModelScope.launch {
updateEntriesSelection(catNums, isSelected)
selectionRepo.updateSelection(catNums, isSelected)
}
private suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
withContext(dispatcher) {
itemsFromRepo.value.let { itemsAll ->
val copiedList = itemsAll.map { item -> item.copy() }
catNums.forEach { catnum ->
copiedList.find { item -> item.catnum == catnum }?.isSelected = isSelected
}
itemsFromRepo.value = copiedList
}
}
}
private suspend fun filterByType(items: List<SatItem>, type: String): List<SatItem> {
return withContext(dispatcher) {
if (type == "All") return@withContext items
val catnums = settingsRepository.loadSatType(type)
if (catnums.isEmpty()) return@withContext items
return@withContext items.filter { item -> item.catnum in catnums }
}
}
private suspend fun filterByQuery(items: List<SatItem>, query: String): List<SatItem> {
return withContext(dispatcher) {
if (query.isBlank()) return@withContext items
return@withContext try {
items.filter { it.catnum == query.toInt() }
} catch (e: Exception) {
items.filter { item -> item.name.lowercase().contains(query.lowercase()) }
}
}
}
fun saveSelection() = viewModelScope.launch { selectionRepo.saveSelection() }
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
initializer {
val container = (this[applicationKey] as MainApplication).container
EntriesViewModel(
container.dataRepository,
container.settingsRepository
)
EntriesViewModel(container.provideSelectionRepository())
}
}
}