mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Further refactoring of entries selection and filtering logic
This commit is contained in:
1 parent
8d8e0bb7ac
commit
76dce66a4a
6 files changed
+96
-113
No files matched your search
@@ -33,9 +33,6 @@ interface SatelliteDao {
|
||||
@Query("SELECT catNum, name, isSelected FROM entries ORDER BY name ASC")
|
||||
fun getAllSatItems(): Flow<List<SatItem>>
|
||||
|
||||
@Query("SELECT * FROM transmitters WHERE isAlive = 1 ORDER BY mode ASC")
|
||||
fun getAllTransmitters(): Flow<List<SatTrans>>
|
||||
|
||||
@Query("SELECT * FROM transmitters WHERE isAlive = 1 and catNum = :catNum")
|
||||
fun getTransmittersByCatNum(catNum: Int): Flow<List<SatTrans>>
|
||||
|
||||
@@ -55,16 +52,9 @@ interface SatelliteDao {
|
||||
|
||||
// Update
|
||||
|
||||
@Transaction
|
||||
suspend fun restoreEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||
clearEntriesSelection()
|
||||
catNums.forEach { catNum ->
|
||||
updateItemSelection(catNum, isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
@Transaction
|
||||
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||
clearEntriesSelection()
|
||||
catNums.forEach { catNum ->
|
||||
updateItemSelection(catNum, isSelected)
|
||||
}
|
||||
|
||||
@@ -52,10 +52,6 @@ class SatelliteRepo @Inject constructor(
|
||||
return satelliteDao.getAllSatItems()
|
||||
}
|
||||
|
||||
fun getAllTransmitters(): Flow<List<SatTrans>> {
|
||||
return satelliteDao.getAllTransmitters()
|
||||
}
|
||||
|
||||
fun getSatTransmitters(catNum: Int): Flow<List<SatTrans>> {
|
||||
return satelliteDao.getTransmittersByCatNum(catNum)
|
||||
}
|
||||
@@ -135,7 +131,7 @@ class SatelliteRepo @Inject constructor(
|
||||
private suspend fun insertEntriesAndRestoreSelection(entries: List<SatEntry>) {
|
||||
val selectedCatNums = satelliteDao.getSelectedCatNums()
|
||||
satelliteDao.insertEntries(entries)
|
||||
satelliteDao.restoreEntriesSelection(selectedCatNums, true)
|
||||
satelliteDao.updateEntriesSelection(selectedCatNums, true)
|
||||
}
|
||||
|
||||
// Update
|
||||
@@ -143,10 +139,4 @@ class SatelliteRepo @Inject constructor(
|
||||
suspend fun updateEntriesSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||
satelliteDao.updateEntriesSelection(catNums, isSelected)
|
||||
}
|
||||
|
||||
// Delete
|
||||
|
||||
suspend fun deleteAllData() {
|
||||
satelliteDao.deleteAllData()
|
||||
}
|
||||
}
|
||||
@@ -34,30 +34,30 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: SatItem, newItem: SatItem): Boolean {
|
||||
return oldItem.isSelected == newItem.isSelected
|
||||
return oldItem.isSelected != newItem.isSelected
|
||||
}
|
||||
}
|
||||
private val listDiffer = AsyncListDiffer(this, diffCallback)
|
||||
private var allItems = emptyList<SatItem>()
|
||||
private val allItems = mutableListOf<SatItem>()
|
||||
private var shouldSelectAll = true
|
||||
private lateinit var entriesClickListener: EntriesClickListener
|
||||
|
||||
interface EntriesClickListener {
|
||||
fun updateSelection(catNums: List<Int>, isSelected: Boolean)
|
||||
}
|
||||
|
||||
fun setEntriesClickListener(listener: EntriesClickListener) {
|
||||
entriesClickListener = listener
|
||||
fun getSelectedIds(): List<Int> {
|
||||
return allItems.filter { it.isSelected }.map { it.catNum }
|
||||
}
|
||||
|
||||
fun submitAllItems(items: List<SatItem>) {
|
||||
allItems = items
|
||||
allItems.clear()
|
||||
allItems.addAll(items)
|
||||
listDiffer.submitList(items)
|
||||
}
|
||||
|
||||
fun selectAllItems() {
|
||||
val catNums = listDiffer.currentList.map { it.catNum }
|
||||
entriesClickListener.updateSelection(catNums, shouldSelectAll)
|
||||
fun selectCurrentItems() {
|
||||
val newList = mutableListOf<SatItem>()
|
||||
listDiffer.currentList.forEach { item ->
|
||||
item.isSelected = shouldSelectAll
|
||||
newList.add(item)
|
||||
}
|
||||
submitCurrentItems(newList)
|
||||
shouldSelectAll = !shouldSelectAll
|
||||
}
|
||||
|
||||
@@ -82,11 +82,11 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
|
||||
submitCurrentItems(allItems.filter { it.catNum == catNum })
|
||||
}
|
||||
|
||||
private fun filterByName(query: String) {
|
||||
val searchQuery = query.toLowerCase(Locale.getDefault())
|
||||
val filteredItems = allItems.filter { satItem ->
|
||||
val lowerCaseItem = satItem.name.toLowerCase(Locale.getDefault())
|
||||
lowerCaseItem.contains(searchQuery)
|
||||
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)
|
||||
}
|
||||
@@ -98,18 +98,16 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>() {
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SatItemHolder, position: Int) {
|
||||
holder.bind(listDiffer.currentList[position], entriesClickListener)
|
||||
holder.bind(listDiffer.currentList[position])
|
||||
}
|
||||
|
||||
class SatItemHolder private constructor(private val binding: ItemSatEntryBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: SatItem, clickListener: EntriesClickListener) {
|
||||
fun bind(item: SatItem) {
|
||||
binding.satItemCheckbox.text = item.name
|
||||
binding.satItemCheckbox.isChecked = item.isSelected
|
||||
itemView.setOnClickListener {
|
||||
clickListener.updateSelection(listOf(item.catNum), item.isSelected.not())
|
||||
}
|
||||
itemView.setOnClickListener { item.isSelected = item.isSelected.not() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
package com.rtbishop.look4sat.ui.entriesScreen
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.View
|
||||
import android.widget.SearchView
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
@@ -31,16 +31,16 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.data.model.Result
|
||||
import com.rtbishop.look4sat.data.model.SatItem
|
||||
import com.rtbishop.look4sat.data.model.TleSource
|
||||
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
|
||||
import com.rtbishop.look4sat.utility.RecyclerDivider
|
||||
import com.rtbishop.look4sat.utility.getNavResult
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@FlowPreview
|
||||
@AndroidEntryPoint
|
||||
class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.EntriesClickListener {
|
||||
class EntriesFragment : Fragment(R.layout.fragment_entries), SearchView.OnQueryTextListener {
|
||||
|
||||
private val viewModel: EntriesViewModel by viewModels()
|
||||
private val filePicker =
|
||||
@@ -52,17 +52,13 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentEntriesBinding.bind(view)
|
||||
setupComponents()
|
||||
observeSatelliteData()
|
||||
observeSourcesResult()
|
||||
setupComponents(view)
|
||||
setupObservers()
|
||||
}
|
||||
|
||||
private fun setupComponents() {
|
||||
entriesAdapter = EntriesAdapter().apply {
|
||||
setEntriesClickListener(this@EntriesFragment)
|
||||
}
|
||||
binding?.apply {
|
||||
private fun setupComponents(view: View) {
|
||||
entriesAdapter = EntriesAdapter()
|
||||
binding = FragmentEntriesBinding.bind(view).apply {
|
||||
entriesRecycler.apply {
|
||||
setHasFixedSize(true)
|
||||
adapter = entriesAdapter
|
||||
@@ -70,39 +66,23 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
addItemDecoration(RecyclerDivider(R.drawable.rec_divider_light))
|
||||
}
|
||||
importWeb.setOnClickListener { showImportFromWebDialog() }
|
||||
importWeb.setOnClickListener { findNavController().navigate(R.id.nav_dialog_sources) }
|
||||
importFile.setOnClickListener { filePicker.launch("*/*") }
|
||||
selectMode.setOnClickListener { showModesDialog() }
|
||||
selectAll.setOnClickListener { entriesAdapter?.selectAllItems() }
|
||||
searchBar.addTextChangedListener { query -> filterByQuery(query) }
|
||||
searchBar.clearFocus()
|
||||
selectAll.setOnClickListener { entriesAdapter?.selectCurrentItems() }
|
||||
entriesSubmit.setOnClickListener { updateEntriesSelection() }
|
||||
searchBar.setOnQueryTextListener(this@EntriesFragment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeSatelliteData() {
|
||||
private fun setupObservers() {
|
||||
viewModel.satData.observe(viewLifecycleOwner, { result ->
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
if (result.data.isEmpty()) {
|
||||
setEmpty()
|
||||
} else {
|
||||
entriesAdapter?.submitAllItems(result.data)
|
||||
binding?.entriesRecycler?.smoothScrollToPosition(0)
|
||||
setLoaded()
|
||||
}
|
||||
}
|
||||
is Result.InProgress -> {
|
||||
setLoading()
|
||||
}
|
||||
is Result.Error -> {
|
||||
val errorMsg = getString(R.string.entries_update_error)
|
||||
Snackbar.make(requireView(), errorMsg, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
is Result.Success -> setLoaded(result.data)
|
||||
is Result.InProgress -> setLoading()
|
||||
is Result.Error -> setError()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun observeSourcesResult() {
|
||||
getNavResult<List<String>>(R.id.nav_entries, "sources") { result ->
|
||||
result.map { TleSource(it) }.let { sources ->
|
||||
if (sources.isNullOrEmpty()) {
|
||||
@@ -114,11 +94,21 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLoaded() {
|
||||
binding?.apply {
|
||||
entriesError.visibility = View.INVISIBLE
|
||||
entriesProgress.visibility = View.INVISIBLE
|
||||
entriesRecycler.visibility = View.VISIBLE
|
||||
private fun setLoaded(items: List<SatItem>) {
|
||||
if (items.isEmpty()) {
|
||||
binding?.apply {
|
||||
entriesError.visibility = View.VISIBLE
|
||||
entriesProgress.visibility = View.INVISIBLE
|
||||
entriesRecycler.visibility = View.INVISIBLE
|
||||
}
|
||||
} else {
|
||||
entriesAdapter?.submitAllItems(items)
|
||||
binding?.apply {
|
||||
entriesError.visibility = View.INVISIBLE
|
||||
entriesProgress.visibility = View.INVISIBLE
|
||||
entriesRecycler.visibility = View.VISIBLE
|
||||
entriesRecycler.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,21 +120,29 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
||||
}
|
||||
}
|
||||
|
||||
private fun setEmpty() {
|
||||
private fun setError() {
|
||||
binding?.apply {
|
||||
entriesError.visibility = View.VISIBLE
|
||||
entriesError.visibility = View.INVISIBLE
|
||||
entriesProgress.visibility = View.INVISIBLE
|
||||
entriesRecycler.visibility = View.INVISIBLE
|
||||
entriesRecycler.visibility = View.VISIBLE
|
||||
}
|
||||
val errorMsg = getString(R.string.entries_update_error)
|
||||
Snackbar.make(requireView(), errorMsg, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun filterByQuery(query: Editable?) {
|
||||
entriesAdapter?.filterItems(query.toString())
|
||||
binding?.entriesRecycler?.smoothScrollToPosition(0)
|
||||
private fun filterAndScroll(query: String) {
|
||||
entriesAdapter?.filterItems(query)
|
||||
binding?.entriesRecycler?.scrollToPosition(0)
|
||||
}
|
||||
|
||||
private fun showImportFromWebDialog() {
|
||||
findNavController().navigate(R.id.nav_dialog_sources)
|
||||
private fun updateEntriesSelection() {
|
||||
setLoading()
|
||||
lifecycleScope.launch {
|
||||
entriesAdapter?.getSelectedIds()?.let { ids ->
|
||||
viewModel.updateEntriesSelection(ids, true)
|
||||
}
|
||||
findNavController().navigate(R.id.nav_passes)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showModesDialog() {
|
||||
@@ -161,10 +159,9 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
||||
MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setTitle(getString(R.string.modes_title))
|
||||
setMultiChoiceItems(modes, savedModes) { _, which, isChecked ->
|
||||
if (isChecked) {
|
||||
selectedModes.add(modes[which])
|
||||
} else if (selectedModes.contains(modes[which])) {
|
||||
selectedModes.remove(modes[which])
|
||||
when {
|
||||
isChecked -> selectedModes.add(modes[which])
|
||||
selectedModes.contains(modes[which]) -> selectedModes.remove(modes[which])
|
||||
}
|
||||
}
|
||||
setPositiveButton(getString(android.R.string.ok)) { _, _ ->
|
||||
@@ -178,8 +175,13 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||
viewModel.updateEntriesSelection(catNums, isSelected)
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
filterAndScroll(newText)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.rtbishop.look4sat.data.model.TleSource
|
||||
import com.rtbishop.look4sat.data.repository.PrefsRepo
|
||||
import com.rtbishop.look4sat.data.repository.SatelliteRepo
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -35,7 +34,6 @@ import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
@FlowPreview
|
||||
@HiltViewModel
|
||||
class EntriesViewModel @Inject constructor(
|
||||
private val prefsRepo: PrefsRepo,
|
||||
@@ -98,9 +96,7 @@ class EntriesViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateEntriesSelection(items: List<Int>, isSelected: Boolean) {
|
||||
viewModelScope.launch {
|
||||
satelliteRepo.updateEntriesSelection(items, isSelected)
|
||||
}
|
||||
suspend fun updateEntriesSelection(items: List<Int>, isSelected: Boolean) {
|
||||
satelliteRepo.updateEntriesSelection(items, isSelected)
|
||||
}
|
||||
}
|
||||
@@ -51,15 +51,13 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
<SearchView
|
||||
android:id="@+id/search_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:hint="@string/entries_search_hint"
|
||||
android:layout_marginStart="8dp"
|
||||
android:iconifiedByDefault="false"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:queryHint="@string/entries_search_hint"
|
||||
app:layout_constraintEnd_toStartOf="@+id/select_mode"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/entries_bar" />
|
||||
@@ -118,5 +116,14 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/entries_submit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center"
|
||||
android:layout_margin="@dimen/fab_padding"
|
||||
android:contentDescription="@string/placeholder"
|
||||
android:src="@drawable/ic_arrow" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
Reference in new issue
Block a user