diff --git a/app/src/main/java/com/rtbishop/look4sat/data/repository/PrefsRepo.kt b/app/src/main/java/com/rtbishop/look4sat/data/repository/PrefsRepo.kt index f6497fb0..8b6fcf7c 100644 --- a/app/src/main/java/com/rtbishop/look4sat/data/repository/PrefsRepo.kt +++ b/app/src/main/java/com/rtbishop/look4sat/data/repository/PrefsRepo.kt @@ -35,6 +35,7 @@ class PrefsRepo @Inject constructor(val preferences: SharedPreferences, moshi: M companion object { const val keySources = "sourcesListJson" + const val keyModes = "satModes" const val keyLatitude = "latitude" const val keyLongitude = "longitude" const val keyAltitude = "altitude" @@ -117,7 +118,7 @@ class PrefsRepo @Inject constructor(val preferences: SharedPreferences, moshi: M val sourcesJson = sourcesAdapter.toJson(sources) preferences.edit { putString(keySources, sourcesJson) } } - + fun loadDefaultSources(): List { return listOf( 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") ) } + + fun saveModesSelection(modes: List) { + val modesSet = modes.toSet() + preferences.edit { + putStringSet(keyModes, modesSet) + } + } + + fun loadModesSelection(): List { + preferences.getStringSet(keyModes, setOf())?.let { modesSet -> + return modesSet.toList().sorted() + } + return emptyList() + } } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt index 9c1c31ef..feb4b51e 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesFragment.kt @@ -27,6 +27,7 @@ import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator +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 @@ -71,6 +72,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr } importWeb.setOnClickListener { showImportFromWebDialog() } importFile.setOnClickListener { filePicker.launch("*/*") } + selectMode.setOnClickListener { showModesDialog() } selectAll.setOnClickListener { entriesAdapter?.selectAllItems() } searchBar.addTextChangedListener { query -> filterByQuery(query) } searchBar.clearFocus() @@ -85,6 +87,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr setEmpty() } else { entriesAdapter?.submitAllItems(result.data) + binding?.entriesRecycler?.smoothScrollToPosition(0) setLoaded() } } @@ -101,11 +104,12 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr private fun observeSourcesResult() { getNavResult>(R.id.nav_entries, "sources") { result -> - val sources = result.map { TleSource(it) } - if (sources.isNullOrEmpty()) { - viewModel.importSatDataFromSources() - } else { - viewModel.importSatDataFromSources(sources) + result.map { TleSource(it) }.let { sources -> + if (sources.isNullOrEmpty()) { + viewModel.importSatDataFromSources() + } else { + viewModel.importSatDataFromSources(sources) + } } } } @@ -134,15 +138,46 @@ class EntriesFragment : Fragment(R.layout.fragment_entries), EntriesAdapter.Entr } } - private fun filterByQuery(it: Editable?) { - entriesAdapter?.filterItems(it.toString()) - binding?.entriesRecycler?.scrollToPosition(0) + private fun filterByQuery(query: Editable?) { + entriesAdapter?.filterItems(query.toString()) + binding?.entriesRecycler?.smoothScrollToPosition(0) } private fun showImportFromWebDialog() { 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, isSelected: Boolean) { viewModel.updateEntriesSelection(catNums, isSelected) } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt index 632789e7..151987d4 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/entriesScreen/EntriesViewModel.kt @@ -28,10 +28,8 @@ 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.MutableSharedFlow -import kotlinx.coroutines.flow.flattenMerge -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -44,31 +42,56 @@ class EntriesViewModel @Inject constructor( private val satelliteRepo: SatelliteRepo ) : ViewModel() { - private val satDataState = MutableSharedFlow>>(replay = 0) - private val satDataFlow = satelliteRepo.getAllSatItems().map { Result.Success(it) } - val satData = - flowOf(satDataState, satDataFlow).flattenMerge().asLiveData(viewModelScope.coroutineContext) + private val allSatItems = mutableListOf() + private val satDataState = MutableStateFlow>>(Result.InProgress) + val satData = satDataState.asLiveData(viewModelScope.coroutineContext) + + init { + viewModelScope.launch { + satelliteRepo.getAllSatItems().collect { items -> + allSatItems.clear() + allSatItems.addAll(items) + filterByModes(getModesSelection()) + } + } + } + + fun getModesSelection(): List { + return prefsRepo.loadModesSelection() + } + + fun filterByModes(modes: List) { + 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) { viewModelScope.launch { - satDataState.emit(Result.InProgress) + satDataState.value = Result.InProgress try { satelliteRepo.importSatDataFromFile(uri) } catch (exception: Exception) { - satDataState.emit(Result.Error(exception)) + satDataState.value = Result.Error(exception) } } } fun importSatDataFromSources(sources: List = prefsRepo.loadDefaultSources()) { viewModelScope.launch { - satDataState.emit(Result.InProgress) + satDataState.value = Result.InProgress val updateMillis = measureTimeMillis { try { prefsRepo.saveTleSources(sources) satelliteRepo.importSatDataFromWeb(sources) } catch (exception: Exception) { - satDataState.emit(Result.Error(exception)) + satDataState.value = Result.Error(exception) } } Timber.d("Update from WEB took $updateMillis ms") diff --git a/app/src/main/res/layout/fragment_entries.xml b/app/src/main/res/layout/fragment_entries.xml index 487d4864..0e38dd08 100644 --- a/app/src/main/res/layout/fragment_entries.xml +++ b/app/src/main/res/layout/fragment_entries.xml @@ -60,10 +60,21 @@ android:iconifiedByDefault="false" android:importantForAutofill="no" android:inputType="text" - app:layout_constraintEnd_toStartOf="@+id/select_all" + app:layout_constraintEnd_toStartOf="@+id/select_mode" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/entries_bar" /> + + Apache-2.0 Политика конфиденциальности \nЭто ПО поставляется без гарантий. \nПодробности в лицензии GNU GPLv3. + Выберите модуляцию трансивера diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3604f7b1..da599135 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -99,4 +99,5 @@ Privacy Policy \nThis app comes with no warranty. \nSee the GNU GPLv3 license for details. + Choose transmitter mode