Switched to SatItems in Entries recycler Adapter

This commit is contained in:
Arty Bishop committed 2021-03-06 15:42:35 +00:00
1 parent 8df2334cd6
commit ab811e4ead
6 files changed
+136 -111

No files matched your search

@@ -0,0 +1,22 @@
/*******************************************************************************
Look4Sat. Amateur radio satellite tracker and pass predictor.
Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
******************************************************************************/
package com.rtbishop.look4sat.data
class SatItem(val catNum: Int, val name: String, var isSelected: Boolean)
@@ -1,20 +1,20 @@
/******************************************************************************* /*******************************************************************************
Look4Sat. Amateur radio satellite tracker and pass predictor. Look4Sat. Amateur radio satellite tracker and pass predictor.
Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com) Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com)
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc., with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
******************************************************************************/ ******************************************************************************/
package com.rtbishop.look4sat.repository package com.rtbishop.look4sat.repository
@@ -23,6 +23,7 @@ import android.content.ContentResolver
import android.net.Uri import android.net.Uri
import com.github.amsacode.predict4java.TLE import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.data.SatEntry import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.data.SatItem
import com.rtbishop.look4sat.data.SatTrans import com.rtbishop.look4sat.data.SatTrans
import com.rtbishop.look4sat.data.TleSource import com.rtbishop.look4sat.data.TleSource
import com.rtbishop.look4sat.di.IoDispatcher import com.rtbishop.look4sat.di.IoDispatcher
@@ -41,8 +42,12 @@ class SatelliteRepo @Inject constructor(
@IoDispatcher private val ioDispatcher: CoroutineDispatcher @IoDispatcher private val ioDispatcher: CoroutineDispatcher
) { ) {
fun getEntries(): Flow<List<SatEntry>> { fun getSatItems(): Flow<List<SatItem>> {
return satelliteDao.getEntries() return satelliteDao.getSatItems()
}
suspend fun getSelectedEntries(): List<SatEntry> {
return satelliteDao.getSelectedEntries()
} }
suspend fun updateEntriesSelection(catNums: List<Int>) { suspend fun updateEntriesSelection(catNums: List<Int>) {
@@ -1,26 +1,27 @@
/******************************************************************************* /*******************************************************************************
Look4Sat. Amateur radio satellite tracker and pass predictor. Look4Sat. Amateur radio satellite tracker and pass predictor.
Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com) Copyright (C) 2019, 2020 Arty Bishop (bishop.arty@gmail.com)
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc., with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
******************************************************************************/ ******************************************************************************/
package com.rtbishop.look4sat.repository.localData package com.rtbishop.look4sat.repository.localData
import androidx.room.* import androidx.room.*
import com.rtbishop.look4sat.data.SatEntry import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.data.SatItem
import com.rtbishop.look4sat.data.SatTrans import com.rtbishop.look4sat.data.SatTrans
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -30,9 +31,15 @@ interface SatelliteDao {
@Query("SELECT * FROM entries ORDER BY name ASC") @Query("SELECT * FROM entries ORDER BY name ASC")
fun getEntries(): Flow<List<SatEntry>> fun getEntries(): Flow<List<SatEntry>>
@Query("SELECT catNum, name, isSelected FROM entries ORDER BY name ASC")
fun getSatItems(): Flow<List<SatItem>>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertEntries(entries: List<SatEntry>) suspend fun insertEntries(entries: List<SatEntry>)
@Query("SELECT * FROM entries WHERE isSelected = 1 ORDER BY name ASC")
suspend fun getSelectedEntries(): List<SatEntry>
@Query("SELECT catNum FROM entries WHERE isSelected = 1") @Query("SELECT catNum FROM entries WHERE isSelected = 1")
suspend fun getSelectedCatNums(): List<Int> suspend fun getSelectedCatNums(): List<Int>
@@ -23,8 +23,8 @@ import android.net.Uri
import androidx.lifecycle.* import androidx.lifecycle.*
import com.github.amsacode.predict4java.SatelliteFactory import com.github.amsacode.predict4java.SatelliteFactory
import com.rtbishop.look4sat.data.* import com.rtbishop.look4sat.data.*
import com.rtbishop.look4sat.repository.SatelliteRepo
import com.rtbishop.look4sat.repository.PrefsRepo import com.rtbishop.look4sat.repository.PrefsRepo
import com.rtbishop.look4sat.repository.SatelliteRepo
import com.rtbishop.look4sat.utility.getPredictor import com.rtbishop.look4sat.utility.getPredictor
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -41,7 +41,6 @@ class SharedViewModel @Inject constructor(
private val _passes = MutableLiveData<Result<MutableList<SatPass>>>() private val _passes = MutableLiveData<Result<MutableList<SatPass>>>()
private val _appEvent = MutableLiveData<Event<Int>>() private val _appEvent = MutableLiveData<Event<Int>>()
private var selectedEntries = emptyList<SatEntry>()
private var shouldTriggerCalculation = true private var shouldTriggerCalculation = true
init { init {
@@ -60,7 +59,7 @@ class SharedViewModel @Inject constructor(
} }
fun getSources() = liveData { emit(prefsRepo.loadTleSources()) } fun getSources() = liveData { emit(prefsRepo.loadTleSources()) }
fun getEntries() = satelliteRepo.getEntries().asLiveData() fun getSatItems() = satelliteRepo.getSatItems().asLiveData()
fun getPasses(): LiveData<Result<MutableList<SatPass>>> = _passes fun getPasses(): LiveData<Result<MutableList<SatPass>>> = _passes
fun getTransmittersForSat(satId: Int) = fun getTransmittersForSat(satId: Int) =
satelliteRepo.getTransmittersForSat(satId).asLiveData() satelliteRepo.getTransmittersForSat(satId).asLiveData()
@@ -77,16 +76,14 @@ class SharedViewModel @Inject constructor(
_passes.value = Result.InProgress _passes.value = Result.InProgress
viewModelScope.launch(Dispatchers.Default) { viewModelScope.launch(Dispatchers.Default) {
val passes = mutableListOf<SatPass>() val passes = mutableListOf<SatPass>()
selectedEntries.forEach { passes.addAll(getPasses(it, dateNow)) } satelliteRepo.getSelectedEntries().forEach { entry ->
passes.addAll(getPasses(entry, dateNow))
}
val filteredPasses = sortList(passes, dateNow) val filteredPasses = sortList(passes, dateNow)
_passes.postValue(Result.Success(filteredPasses)) _passes.postValue(Result.Success(filteredPasses))
} }
} }
fun setEntries(entries: List<SatEntry>) {
selectedEntries = entries.filter { it.isSelected }
}
fun updateEntriesFromFile(uri: Uri) { fun updateEntriesFromFile(uri: Uri) {
postAppEvent(Event(0)) postAppEvent(Event(0))
viewModelScope.launch { viewModelScope.launch {
@@ -110,15 +107,16 @@ class SharedViewModel @Inject constructor(
} }
} }
} }
fun updateEntriesSelection(entries: List<SatEntry>) { fun updateItemsSelection(items: List<SatItem>) {
_passes.value = Result.InProgress
viewModelScope.launch { viewModelScope.launch {
selectedEntries = entries.filter { it.isSelected } val selectedEntries = items.filter { it.isSelected }.map { it.catNum }
satelliteRepo.updateEntriesSelection(selectedEntries)
calculatePasses() calculatePasses()
satelliteRepo.updateEntriesSelection(selectedEntries.map { it.catNum })
} }
} }
private fun postAppEvent(event: Event<Int>) { private fun postAppEvent(event: Event<Int>) {
_appEvent.value = event _appEvent.value = event
} }
@@ -23,40 +23,47 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.SearchView import android.widget.SearchView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.data.SatEntry import com.rtbishop.look4sat.data.SatItem
import com.rtbishop.look4sat.databinding.ItemSatEntryBinding import com.rtbishop.look4sat.databinding.ItemSatEntryBinding
import java.util.* import java.util.*
class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatEntryHolder>(), class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatItemHolder>(),
SearchView.OnQueryTextListener { SearchView.OnQueryTextListener {
private val allItems = mutableListOf<SatItem>()
private val currentItems = mutableListOf<SatItem>()
private var shouldSearchAll = true private var shouldSearchAll = true
private var allEntries = mutableListOf<SatEntry>()
private var currentEntries = mutableListOf<SatEntry>() fun getItems(): List<SatItem> {
return allItems
fun getEntries(): MutableList<SatEntry> {
return allEntries
} }
fun setEntries(list: MutableList<SatEntry>) { fun setItems(items: List<SatItem>) {
allEntries = list allItems.clear()
currentEntries = list allItems.addAll(items)
currentItems.clear()
currentItems.addAll(items)
notifyDataSetChanged() notifyDataSetChanged()
} }
fun selectAll() { fun selectAllItems() {
shouldSearchAll = if (shouldSearchAll) { shouldSearchAll = shouldSearchAll.not()
currentEntries.forEach { it.isSelected = true } currentItems.forEach { it.isSelected = it.isSelected.not() }
notifyDataSetChanged() notifyDataSetChanged()
false
} else {
currentEntries.forEach { it.isSelected = false }
notifyDataSetChanged()
true
}
} }
private fun filterEntries(list: MutableList<SatEntry>, query: String): MutableList<SatEntry> { override fun onQueryTextChange(newText: String): Boolean {
currentItems.clear()
currentItems.addAll(filterItems(allItems, newText).toMutableList())
notifyDataSetChanged()
return false
}
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
private fun filterItems(list: List<SatItem>, query: String): List<SatItem> {
if (query.isEmpty()) return list if (query.isEmpty()) return list
return try { return try {
filterByCatNum(list, query.toInt()) filterByCatNum(list, query.toInt())
@@ -64,54 +71,42 @@ class EntriesAdapter : RecyclerView.Adapter<EntriesAdapter.SatEntryHolder>(),
filterByName(list, query) filterByName(list, query)
} }
} }
private fun filterByCatNum(list: MutableList<SatEntry>, catNum: Int): MutableList<SatEntry> { private fun filterByCatNum(list: List<SatItem>, catNum: Int): List<SatItem> {
return list.filter { it.catNum == catNum } as MutableList return list.filter { it.catNum == catNum }
} }
private fun filterByName(list: MutableList<SatEntry>, query: String): MutableList<SatEntry> { private fun filterByName(list: List<SatItem>, query: String): List<SatItem> {
val defaultLocale = Locale.getDefault() val defaultLocale = Locale.getDefault()
val filteredList = mutableListOf<SatEntry>()
val searchQuery = query.toLowerCase(defaultLocale) val searchQuery = query.toLowerCase(defaultLocale)
list.forEach { return list.filter { satItem ->
val entryName = it.name.toLowerCase(defaultLocale) val lowerCaseItem = satItem.name.toLowerCase(defaultLocale)
if (entryName.contains(searchQuery)) filteredList.add(it) lowerCaseItem.contains(searchQuery)
} }
return filteredList
} }
override fun onQueryTextChange(newText: String): Boolean { override fun getItemCount(): Int {
currentEntries = filterEntries(getEntries(), newText) return currentItems.size
notifyDataSetChanged()
return false
} }
override fun onQueryTextSubmit(query: String): Boolean { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SatItemHolder {
return false
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SatEntryHolder {
val binding = ItemSatEntryBinding val binding = ItemSatEntryBinding
.inflate(LayoutInflater.from(parent.context), parent, false) .inflate(LayoutInflater.from(parent.context), parent, false)
return SatEntryHolder(binding) return SatItemHolder(binding)
} }
override fun onBindViewHolder(holder: SatEntryHolder, position: Int) { override fun onBindViewHolder(holder: SatItemHolder, position: Int) {
holder.bind(currentEntries[position]) holder.bind(currentItems[position])
} }
override fun getItemCount(): Int { inner class SatItemHolder(private val binding: ItemSatEntryBinding) :
return currentEntries.size
}
inner class SatEntryHolder(private val binding: ItemSatEntryBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(satEntry: SatEntry) { fun bind(satItem: SatItem) {
binding.satEntryCheckbox.text = satEntry.name binding.satEntryCheckbox.text = satItem.name
binding.satEntryCheckbox.isChecked = satEntry.isSelected binding.satEntryCheckbox.isChecked = satItem.isSelected
itemView.setOnClickListener { itemView.setOnClickListener {
satEntry.isSelected = binding.satEntryCheckbox.isChecked satItem.isSelected = binding.satEntryCheckbox.isChecked
} }
} }
} }
@@ -29,7 +29,6 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.SatEntry
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
import com.rtbishop.look4sat.ui.SharedViewModel import com.rtbishop.look4sat.ui.SharedViewModel
import com.rtbishop.look4sat.utility.RecyclerDivider import com.rtbishop.look4sat.utility.RecyclerDivider
@@ -66,7 +65,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
} }
importWeb.setOnClickListener { showImportFromWebDialog() } importWeb.setOnClickListener { showImportFromWebDialog() }
importFile.setOnClickListener { filePicker.launch("*/*") } importFile.setOnClickListener { filePicker.launch("*/*") }
selectAll.setOnClickListener { entriesAdapter?.selectAll() } selectAll.setOnClickListener { entriesAdapter?.selectAllItems() }
entriesFab.setOnClickListener { navigateToPasses() } entriesFab.setOnClickListener { navigateToPasses() }
searchBar.setOnQueryTextListener(entriesAdapter) searchBar.setOnQueryTextListener(entriesAdapter)
searchBar.clearFocus() searchBar.clearFocus()
@@ -75,11 +74,10 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
private fun observeEntries() { private fun observeEntries() {
setLoading() setLoading()
viewModel.getEntries().observe(viewLifecycleOwner, { entries -> viewModel.getSatItems().observe(viewLifecycleOwner, { satItems ->
if (entries.isNullOrEmpty()) setError() if (satItems.isNullOrEmpty()) setError()
else { else {
viewModel.setEntries(entries) entriesAdapter?.setItems(satItems)
entriesAdapter?.setEntries(entries as MutableList<SatEntry>)
setLoaded() setLoaded()
} }
observeEvents() observeEvents()
@@ -130,9 +128,9 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
private fun navigateToPasses() { private fun navigateToPasses() {
binding?.searchBar?.clearFocus() binding?.searchBar?.clearFocus()
entriesAdapter?.let { entriesAdapter?.let {
val entries = it.getEntries() val satItems = it.getItems()
if (entries.isNotEmpty()) { if (satItems.isNotEmpty()) {
viewModel.updateEntriesSelection(entries) viewModel.updateItemsSelection(satItems)
requireView().findNavController().navigate(R.id.action_entries_to_passes) requireView().findNavController().navigate(R.id.action_entries_to_passes)
} }
} }