Added sat items filtering by transmitter mode
This commit is contained in:
1 parent
995953b823
commit
8d8e0bb7ac
6 files changed
+108
-22
No files matched your search
@@ -35,6 +35,7 @@ class PrefsRepo @Inject constructor(val preferences: SharedPreferences, moshi: M
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val keySources = "sourcesListJson"
|
const val keySources = "sourcesListJson"
|
||||||
|
const val keyModes = "satModes"
|
||||||
const val keyLatitude = "latitude"
|
const val keyLatitude = "latitude"
|
||||||
const val keyLongitude = "longitude"
|
const val keyLongitude = "longitude"
|
||||||
const val keyAltitude = "altitude"
|
const val keyAltitude = "altitude"
|
||||||
@@ -117,7 +118,7 @@ class PrefsRepo @Inject constructor(val preferences: SharedPreferences, moshi: M
|
|||||||
val sourcesJson = sourcesAdapter.toJson(sources)
|
val sourcesJson = sourcesAdapter.toJson(sources)
|
||||||
preferences.edit { putString(keySources, sourcesJson) }
|
preferences.edit { putString(keySources, sourcesJson) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadDefaultSources(): List<TleSource> {
|
fun loadDefaultSources(): List<TleSource> {
|
||||||
return listOf(
|
return listOf(
|
||||||
TleSource("https://celestrak.com/NORAD/elements/active.txt"),
|
TleSource("https://celestrak.com/NORAD/elements/active.txt"),
|
||||||
@@ -126,4 +127,18 @@ class PrefsRepo @Inject constructor(val preferences: SharedPreferences, moshi: M
|
|||||||
TleSource("https://www.prismnet.com/~mmccants/tles/inttles.zip")
|
TleSource("https://www.prismnet.com/~mmccants/tles/inttles.zip")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveModesSelection(modes: List<String>) {
|
||||||
|
val modesSet = modes.toSet()
|
||||||
|
preferences.edit {
|
||||||
|
putStringSet(keyModes, modesSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadModesSelection(): List<String> {
|
||||||
|
preferences.getStringSet(keyModes, setOf())?.let { modesSet ->
|
||||||
|
return modesSet.toList().sorted()
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,7 @@ import androidx.fragment.app.viewModels
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
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.model.Result
|
import com.rtbishop.look4sat.data.model.Result
|
||||||
@@ -71,6 +72,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
|||||||
}
|
}
|
||||||
importWeb.setOnClickListener { showImportFromWebDialog() }
|
importWeb.setOnClickListener { showImportFromWebDialog() }
|
||||||
importFile.setOnClickListener { filePicker.launch("*/*") }
|
importFile.setOnClickListener { filePicker.launch("*/*") }
|
||||||
|
selectMode.setOnClickListener { showModesDialog() }
|
||||||
selectAll.setOnClickListener { entriesAdapter?.selectAllItems() }
|
selectAll.setOnClickListener { entriesAdapter?.selectAllItems() }
|
||||||
searchBar.addTextChangedListener { query -> filterByQuery(query) }
|
searchBar.addTextChangedListener { query -> filterByQuery(query) }
|
||||||
searchBar.clearFocus()
|
searchBar.clearFocus()
|
||||||
@@ -85,6 +87,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
|||||||
setEmpty()
|
setEmpty()
|
||||||
} else {
|
} else {
|
||||||
entriesAdapter?.submitAllItems(result.data)
|
entriesAdapter?.submitAllItems(result.data)
|
||||||
|
binding?.entriesRecycler?.smoothScrollToPosition(0)
|
||||||
setLoaded()
|
setLoaded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,11 +104,12 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
|||||||
|
|
||||||
private fun observeSourcesResult() {
|
private fun observeSourcesResult() {
|
||||||
getNavResult<List<String>>(R.id.nav_entries, "sources") { result ->
|
getNavResult<List<String>>(R.id.nav_entries, "sources") { result ->
|
||||||
val sources = result.map { TleSource(it) }
|
result.map { TleSource(it) }.let { sources ->
|
||||||
if (sources.isNullOrEmpty()) {
|
if (sources.isNullOrEmpty()) {
|
||||||
viewModel.importSatDataFromSources()
|
viewModel.importSatDataFromSources()
|
||||||
} else {
|
} else {
|
||||||
viewModel.importSatDataFromSources(sources)
|
viewModel.importSatDataFromSources(sources)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,15 +138,46 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun filterByQuery(it: Editable?) {
|
private fun filterByQuery(query: Editable?) {
|
||||||
entriesAdapter?.filterItems(it.toString())
|
entriesAdapter?.filterItems(query.toString())
|
||||||
binding?.entriesRecycler?.scrollToPosition(0)
|
binding?.entriesRecycler?.smoothScrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showImportFromWebDialog() {
|
private fun showImportFromWebDialog() {
|
||||||
findNavController().navigate(R.id.nav_dialog_sources)
|
findNavController().navigate(R.id.nav_dialog_sources)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showModesDialog() {
|
||||||
|
val modes = arrayOf(
|
||||||
|
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
|
||||||
|
"CERTO", "CW", "DQPSK", "DSTAR", "DUV", "FFSK", "FM", "FMN", "FSK", "FSK AX.100 Mode 5",
|
||||||
|
"FSK AX.100 Mode 6", "FSK AX.25 G3RUH", "GFSK", "GFSK Rktr", "GMSK", "HRPT", "LoRa",
|
||||||
|
"LRPT", "LSB", "MFSK", "MSK", "MSK AX.100 Mode 5", "MSK AX.100 Mode 6", "OFDM", "OQPSK",
|
||||||
|
"PSK", "PSK31", "PSK63", "QPSK", "QPSK31", "QPSK63", "SSTV", "USB", "WSJT"
|
||||||
|
)
|
||||||
|
val savedModes = BooleanArray(modes.size)
|
||||||
|
val selectedModes = viewModel.getModesSelection().toMutableList()
|
||||||
|
selectedModes.forEach { savedModes[modes.indexOf(it)] = true }
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPositiveButton(getString(android.R.string.ok)) { _, _ ->
|
||||||
|
viewModel.filterByModes(selectedModes)
|
||||||
|
}
|
||||||
|
setNeutralButton(getString(android.R.string.cancel)) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
create()
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
override fun updateSelection(catNums: List<Int>, isSelected: Boolean) {
|
||||||
viewModel.updateEntriesSelection(catNums, isSelected)
|
viewModel.updateEntriesSelection(catNums, isSelected)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,8 @@ 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.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.flattenMerge
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -44,31 +42,56 @@ class EntriesViewModel @Inject constructor(
|
|||||||
private val satelliteRepo: SatelliteRepo
|
private val satelliteRepo: SatelliteRepo
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val satDataState = MutableSharedFlow<Result<List<SatItem>>>(replay = 0)
|
private val allSatItems = mutableListOf<SatItem>()
|
||||||
private val satDataFlow = satelliteRepo.getAllSatItems().map { Result.Success(it) }
|
private val satDataState = MutableStateFlow<Result<List<SatItem>>>(Result.InProgress)
|
||||||
val satData =
|
val satData = satDataState.asLiveData(viewModelScope.coroutineContext)
|
||||||
flowOf(satDataState, satDataFlow).flattenMerge().asLiveData(viewModelScope.coroutineContext)
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
satelliteRepo.getAllSatItems().collect { items ->
|
||||||
|
allSatItems.clear()
|
||||||
|
allSatItems.addAll(items)
|
||||||
|
filterByModes(getModesSelection())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getModesSelection(): List<String> {
|
||||||
|
return prefsRepo.loadModesSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filterByModes(modes: List<String>) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
fun importSatDataFromFile(uri: Uri) {
|
fun importSatDataFromFile(uri: Uri) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
satDataState.emit(Result.InProgress)
|
satDataState.value = Result.InProgress
|
||||||
try {
|
try {
|
||||||
satelliteRepo.importSatDataFromFile(uri)
|
satelliteRepo.importSatDataFromFile(uri)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
satDataState.emit(Result.Error(exception))
|
satDataState.value = Result.Error(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun importSatDataFromSources(sources: List<TleSource> = prefsRepo.loadDefaultSources()) {
|
fun importSatDataFromSources(sources: List<TleSource> = prefsRepo.loadDefaultSources()) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
satDataState.emit(Result.InProgress)
|
satDataState.value = Result.InProgress
|
||||||
val updateMillis = measureTimeMillis {
|
val updateMillis = measureTimeMillis {
|
||||||
try {
|
try {
|
||||||
prefsRepo.saveTleSources(sources)
|
prefsRepo.saveTleSources(sources)
|
||||||
satelliteRepo.importSatDataFromWeb(sources)
|
satelliteRepo.importSatDataFromWeb(sources)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
satDataState.emit(Result.Error(exception))
|
satDataState.value = Result.Error(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.d("Update from WEB took $updateMillis ms")
|
Timber.d("Update from WEB took $updateMillis ms")
|
||||||
|
|||||||
@@ -60,10 +60,21 @@
|
|||||||
android:iconifiedByDefault="false"
|
android:iconifiedByDefault="false"
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/select_all"
|
app:layout_constraintEnd_toStartOf="@+id/select_mode"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/entries_bar" />
|
app:layout_constraintTop_toBottomOf="@+id/entries_bar" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/select_mode"
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:backgroundTint="@color/greySurface"
|
||||||
|
android:contentDescription="@string/placeholder"
|
||||||
|
android:src="@drawable/ic_satellite"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/select_all"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/select_all"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/select_all" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/select_all"
|
android:id="@+id/select_all"
|
||||||
android:layout_width="64dp"
|
android:layout_width="64dp"
|
||||||
|
|||||||
@@ -79,4 +79,5 @@
|
|||||||
к ним по лицензии <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></string>
|
к ним по лицензии <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></string>
|
||||||
<string name="info_warranty"><a href="https://sites.google.com/view/look4sat-privacy-policy/home">Политика конфиденциальности</a>
|
<string name="info_warranty"><a href="https://sites.google.com/view/look4sat-privacy-policy/home">Политика конфиденциальности</a>
|
||||||
\nЭто ПО поставляется без гарантий. \nПодробности в лицензии <a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GPLv3</a>.</string>
|
\nЭто ПО поставляется без гарантий. \nПодробности в лицензии <a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GPLv3</a>.</string>
|
||||||
|
<string name="modes_title">Выберите модуляцию трансивера</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -99,4 +99,5 @@
|
|||||||
</string>
|
</string>
|
||||||
<string name="info_warranty"><a href="https://sites.google.com/view/look4sat-privacy-policy/home">Privacy Policy</a>
|
<string name="info_warranty"><a href="https://sites.google.com/view/look4sat-privacy-policy/home">Privacy Policy</a>
|
||||||
\nThis app comes with no warranty. \nSee the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GPLv3</a> license for details.</string>
|
\nThis app comes with no warranty. \nSee the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GPLv3</a> license for details.</string>
|
||||||
|
<string name="modes_title">Choose transmitter mode</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in new issue
Block a user