Now sorting sat items list via LiveData switchMap

This commit is contained in:
Arty Bishop committed 2021-03-29 23:11:08 +01:00
1 parent aef560942f
commit 2044ea273b
5 files changed
+48 -68

No files matched your search

@@ -34,7 +34,7 @@ interface SatelliteDao {
@Transaction @Transaction
@Query("SELECT catNum, name, isSelected FROM entries ORDER BY name ASC") @Query("SELECT catNum, name, isSelected FROM entries ORDER BY name ASC")
suspend fun getAllSatItems(): List<SatItem> fun getItemsFlow(): Flow<List<SatItem>>
@Query("SELECT tle FROM entries WHERE isSelected = 1") @Query("SELECT tle FROM entries WHERE isSelected = 1")
suspend fun getSelectedSatellites(): List<Satellite> suspend fun getSelectedSatellites(): List<Satellite>
@@ -52,8 +52,8 @@ class SatelliteRepo @Inject constructor(
return satelliteDao.getTransmittersByCatNum(catNum) return satelliteDao.getTransmittersByCatNum(catNum)
} }
suspend fun getAllSatItems(): List<SatItem> { fun getItemsFlow(): Flow<List<SatItem>> {
return satelliteDao.getAllSatItems() return satelliteDao.getItemsFlow()
} }
suspend fun getSelectedSatellites(): List<Satellite> { suspend fun getSelectedSatellites(): List<Satellite> {
@@ -24,7 +24,6 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.data.model.SatItem import com.rtbishop.look4sat.data.model.SatItem
import com.rtbishop.look4sat.databinding.ItemSatEntryBinding import com.rtbishop.look4sat.databinding.ItemSatEntryBinding
import java.util.*
class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() { class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
@@ -67,36 +66,10 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
shouldSelectAll = !shouldSelectAll shouldSelectAll = !shouldSelectAll
} }
fun filterItems(query: String) {
if (query.isEmpty()) {
submitCurrentItems(allItems)
} else {
try {
filterByCatNum(query.toInt())
} catch (e: NumberFormatException) {
filterByName(query)
}
}
shouldSelectAll = true
}
private fun submitCurrentItems(items: List<SatItem>) { private fun submitCurrentItems(items: List<SatItem>) {
listDiffer.submitList(items) listDiffer.submitList(items)
} }
private fun filterByCatNum(catNum: Int) {
submitCurrentItems(allItems.filter { it.catNum == catNum })
}
private fun filterByName(name: String) {
val satName = name.toLowerCase(Locale.getDefault())
val filteredItems = allItems.filter { item ->
val itemName = item.name.toLowerCase(Locale.getDefault())
itemName.contains(satName)
}
submitCurrentItems(filteredItems)
}
override fun getItemCount(): Int = listDiffer.currentList.size override fun getItemCount(): Int = listDiffer.currentList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SatItemHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SatItemHolder {
@@ -114,7 +87,6 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
binding.satItemCheckbox.text = item.name binding.satItemCheckbox.text = item.name
binding.satItemCheckbox.isChecked = item.isSelected binding.satItemCheckbox.isChecked = item.isSelected
itemView.setOnClickListener { itemView.setOnClickListener {
item.isSelected = item.isSelected.not()
listener.updateSelection(listOf(item.catNum), item.isSelected.not()) listener.updateSelection(listOf(item.catNum), item.isSelected.not())
} }
} }
@@ -137,14 +137,13 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), SearchView.OnQueryT
} }
override fun onQueryTextChange(newText: String): Boolean { override fun onQueryTextChange(newText: String): Boolean {
entriesAdapter?.filterItems(newText) viewModel.setNewQuery(newText)
binding?.entriesRecycler?.scrollToPosition(0)
return true return true
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView()
entriesAdapter = null entriesAdapter = null
binding = null binding = null
super.onDestroyView()
} }
} }
@@ -20,9 +20,7 @@ package com.rtbishop.look4sat.ui.entriesScreen
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModel import androidx.lifecycle.*
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.model.Result import com.rtbishop.look4sat.data.model.Result
@@ -31,9 +29,10 @@ import com.rtbishop.look4sat.data.model.TleSource
import com.rtbishop.look4sat.data.repository.PrefsRepo import com.rtbishop.look4sat.data.repository.PrefsRepo
import com.rtbishop.look4sat.data.repository.SatelliteRepo import com.rtbishop.look4sat.data.repository.SatelliteRepo
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@@ -43,55 +42,39 @@ class EntriesViewModel @Inject constructor(
private val satelliteRepo: SatelliteRepo private val satelliteRepo: SatelliteRepo
) : ViewModel(), EntriesAdapter.EntriesClickListener { ) : ViewModel(), EntriesAdapter.EntriesClickListener {
private var allSatItems = listOf<SatItem>() private val transModes = MutableLiveData(prefsRepo.loadModesSelection())
private val satDataState = MutableStateFlow<Result<List<SatItem>>>(Result.InProgress) private val currentQuery = MutableLiveData(String())
val satData = satDataState.asLiveData(viewModelScope.coroutineContext) private val itemsWithModes = transModes.switchMap { modes ->
liveData { satelliteRepo.getItemsFlow().collect { emit(filterByModes(it, modes)) } }
init {
loadAndFilterData()
} }
private val itemsWithQuery = currentQuery.switchMap { query ->
private fun loadAndFilterData() { itemsWithModes.map { items -> Result.Success(filterByQuery(items, query)) }
viewModelScope.launch {
allSatItems = satelliteRepo.getAllSatItems()
filterByModes(prefsRepo.loadModesSelection())
}
} }
private val _satData = MediatorLiveData<Result<List<SatItem>>>().apply {
private fun filterByModes(modes: List<String>) { addSource(itemsWithQuery) { value -> this.value = value }
if (modes.isEmpty()) {
satDataState.value = Result.Success(allSatItems)
} else {
val itemsWithModes = allSatItems.filter { item ->
item.modes.any { mode -> mode in modes }
}
satDataState.value = Result.Success(itemsWithModes)
}
prefsRepo.saveModesSelection(modes)
} }
val satData: LiveData<Result<List<SatItem>>> = _satData
fun importSatDataFromFile(uri: Uri) { fun importSatDataFromFile(uri: Uri) {
viewModelScope.launch { viewModelScope.launch {
satDataState.value = Result.InProgress _satData.value = Result.InProgress
try { try {
satelliteRepo.importSatDataFromFile(uri) satelliteRepo.importSatDataFromFile(uri)
loadAndFilterData()
} catch (exception: Exception) { } catch (exception: Exception) {
satDataState.value = Result.Error(exception) _satData.value = Result.Error(exception)
} }
} }
} }
fun importSatDataFromSources(sources: List<TleSource> = prefsRepo.loadDefaultSources()) { fun importSatDataFromSources(sources: List<TleSource> = prefsRepo.loadDefaultSources()) {
viewModelScope.launch { viewModelScope.launch {
satDataState.value = Result.InProgress _satData.value = Result.InProgress
val updateMillis = measureTimeMillis { val updateMillis = measureTimeMillis {
try { try {
prefsRepo.saveTleSources(sources) prefsRepo.saveTleSources(sources)
satelliteRepo.importSatDataFromWeb(sources) satelliteRepo.importSatDataFromWeb(sources)
loadAndFilterData()
} catch (exception: Exception) { } catch (exception: Exception) {
satDataState.value = Result.Error(exception) _satData.value = Result.Error(exception)
} }
} }
Timber.d("Update from WEB took $updateMillis ms") Timber.d("Update from WEB took $updateMillis ms")
@@ -118,7 +101,7 @@ class EntriesViewModel @Inject constructor(
} }
} }
setPositiveButton(context.getString(android.R.string.ok)) { _, _ -> setPositiveButton(context.getString(android.R.string.ok)) { _, _ ->
filterByModes(selectedModes) setNewModes(selectedModes)
} }
setNeutralButton(context.getString(android.R.string.cancel)) { dialog, _ -> setNeutralButton(context.getString(android.R.string.cancel)) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
@@ -127,6 +110,32 @@ class EntriesViewModel @Inject constructor(
return dialogBuilder.create() return dialogBuilder.create()
} }
fun setNewQuery(newQuery: String) {
currentQuery.value = newQuery
}
private fun setNewModes(newModes: List<String>) {
transModes.value = newModes
prefsRepo.saveModesSelection(newModes)
}
private fun filterByModes(items: List<SatItem>, modes: List<String>): List<SatItem> {
if (modes.isEmpty()) return items
return items.filter { item -> item.modes.any { mode -> mode in modes } }
}
private fun filterByQuery(items: List<SatItem>, query: String): List<SatItem> {
if (query.isBlank()) return items
return try {
items.filter { it.catNum == query.toInt() }
} catch (e: Exception) {
items.filter { item ->
val itemName = item.name.toLowerCase(Locale.getDefault())
itemName.contains(query.toLowerCase(Locale.getDefault()))
}
}
}
override fun updateSelection(catNums: List<Int>, isSelected: Boolean) { override fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
viewModelScope.launch { viewModelScope.launch {
satelliteRepo.updateEntriesSelection(catNums, isSelected) satelliteRepo.updateEntriesSelection(catNums, isSelected)