Swapped modes alert dialog for a custom dialog fragment

This commit is contained in:
Arty Bishop committed 2022-02-19 12:11:50 +00:00
1 parent 97e6f8b7f5
commit dc680231ea
8 files changed
+247 -42

No files matched your search

@@ -19,19 +19,20 @@ package com.rtbishop.look4sat.presentation.entriesScreen
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.snackbar.Snackbar
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.presentation.getNavResult
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint
@@ -59,13 +60,19 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
this.layoutManager = layoutManager
addItemDecoration(itemDecoration)
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0 && entriesFab.visibility == View.VISIBLE) entriesFab.hide()
else if (dy < 0 && entriesFab.visibility != View.VISIBLE) entriesFab.show()
}
})
}
entriesBackBtn.setOnClickListener { findNavController().navigateUp() }
entriesSearch.doOnTextChanged { text, _, _, _ -> viewModel.setQuery(text.toString()) }
entriesModesBtn.setOnClickListener { showModesDialog() }
entriesModesBtn.setOnClickListener { navigateToModesDialog() }
entriesSelectBtn.setOnClickListener { viewModel.selectCurrentItems(true) }
entriesClearBtn.setOnClickListener { viewModel.selectCurrentItems(false) }
entriesAcceptBtn.setOnClickListener {
entriesFab.setOnClickListener {
setNavResult("selection", viewModel.saveSelection())
findNavController().popBackStack()
}
@@ -73,6 +80,9 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
viewModel.satData.observe(viewLifecycleOwner) { satData ->
handleSatData(satData, binding, adapter)
}
getNavResult<List<String>>(R.id.nav_entries, "modes") { modes ->
viewModel.saveSelectedModes(modes)
}
}
private fun handleSatData(
@@ -100,32 +110,8 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
}
}
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.loadSelectedModes().toMutableList()
selectedModes.forEach { savedModes[modes.indexOf(it)] = true }
AlertDialog.Builder(requireContext()).apply {
setTitle(context.getString(R.string.modes_title))
setMultiChoiceItems(modes, savedModes) { _, which, isChecked ->
when {
isChecked -> selectedModes.add(modes[which])
selectedModes.contains(modes[which]) -> selectedModes.remove(modes[which])
}
}
setPositiveButton(context.getString(android.R.string.ok)) { _, _ ->
viewModel.saveSelectedModes(selectedModes)
}
setNeutralButton(context.getString(android.R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}
create().show()
}
private fun navigateToModesDialog() {
val direction = EntriesFragmentDirections.actionEntriesToModes()
findNavController().navigate(direction)
}
}
@@ -41,35 +41,26 @@ class EntriesViewModel @Inject constructor(
private val itemsWithQuery = currentQuery.switchMap { query ->
itemsWithModes.map { items -> filterByQuery(items, query) }
}
// private var shouldSelectAll = true
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
fun selectCurrentItems(selectAll: Boolean) {
itemsWithQuery.value?.let { itemsWithQuery ->
updateSelection(itemsWithQuery.map { item -> item.catnum }, selectAll)
// shouldSelectAll = shouldSelectAll.not()
}
}
fun loadSelectedModes(): List<String> {
return preferences.loadModesSelection()
}
fun saveSelectedModes(modes: List<String>) {
transModes.value = modes
preferences.saveModesSelection(modes)
}
fun setQuery(query: String) {
// shouldSelectAll = true
currentQuery.value = query
}
fun saveSelection(): List<Int> {
return itemsFromRepo.value?.let { itemsAll ->
val filteredItems = itemsAll.filter { item -> item.isSelected }
// repository.setEntriesSelection(filteredItems.map { item -> item.catnum })
filteredItems.map { item -> item.catnum }
itemsAll.filter { item -> item.isSelected }.map { item -> item.catnum }
} ?: emptyList()
}
@@ -0,0 +1,62 @@
package com.rtbishop.look4sat.presentation.entriesScreen
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.databinding.ItemModeBinding
class ModesAdapter(private val clickListener: ModesClickListener) :
RecyclerView.Adapter<ModesAdapter.ModeHolder>() {
private val diffCallback = object : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
}
private val differ = AsyncListDiffer(this, diffCallback)
private var selectedModes = listOf<String>()
fun submitModes(items: List<String>, selected: List<String>) {
differ.submitList(items)
selectedModes = selected
}
override fun getItemCount(): Int = differ.currentList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ModeHolder {
return ModeHolder.from(parent)
}
override fun onBindViewHolder(holder: ModeHolder, position: Int) {
val currentItem = differ.currentList[position]
holder.bind(currentItem, selectedModes.contains(currentItem), clickListener)
}
class ModeHolder(private val binding: ItemModeBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(mode: String, isSelected: Boolean, listener: ModesClickListener) {
binding.modeCheckbox.text = mode
binding.modeCheckbox.isChecked = isSelected
binding.modeCheckbox.setOnClickListener {
listener.onModeClicked(mode, isSelected.not())
}
}
companion object {
fun from(parent: ViewGroup): ModeHolder {
val inflater = LayoutInflater.from(parent.context)
return ModeHolder(ItemModeBinding.inflate(inflater, parent, false))
}
}
}
interface ModesClickListener {
fun onModeClicked(mode: String, isSelected: Boolean)
}
}
@@ -0,0 +1,72 @@
package com.rtbishop.look4sat.presentation.entriesScreen
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.ISettingsHandler
import com.rtbishop.look4sat.databinding.DialogModesBinding
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class ModesDialog : AppCompatDialogFragment(), ModesAdapter.ModesClickListener {
@Inject
lateinit var preferences: ISettingsHandler
private lateinit var binding: DialogModesBinding
private val allModes = listOf(
"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"
)
private val selectedModes = mutableListOf<String>()
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View {
binding = DialogModesBinding.inflate(inflater, group, false)
return binding.root
}
override fun onViewCreated(view: View, state: Bundle?) {
super.onViewCreated(view, state)
dialog?.window?.setBackgroundDrawableResource(R.color.transparent)
dialog?.window?.setLayout(
(resources.displayMetrics.widthPixels * 0.94).toInt(),
(resources.displayMetrics.heightPixels * 0.76).toInt()
)
val modesAdapter = ModesAdapter(this@ModesDialog)
val layoutManager = GridLayoutManager(context, 2)
val itemDecoration = DividerItemDecoration(context, layoutManager.orientation)
selectedModes.addAll(preferences.loadModesSelection())
modesAdapter.submitModes(allModes, selectedModes)
binding.run {
modesRecycler.apply {
setHasFixedSize(true)
this.adapter = modesAdapter
this.layoutManager = layoutManager
addItemDecoration(itemDecoration)
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}
modesBtnNeg.setOnClickListener { dismiss() }
modesBtnPos.setOnClickListener {
setNavResult("modes", selectedModes.toList())
dismiss()
}
}
}
override fun onModeClicked(mode: String, isSelected: Boolean) {
when {
isSelected -> selectedModes.add(mode)
selectedModes.contains(mode) -> selectedModes.remove(mode)
}
}
}
+68
View File
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/SurfaceCard"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/modes_title"
style="@style/DialogTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/sources_title"
android:minHeight="48dp"
app:layout_constraintEnd_toEndOf="@+id/modes_btn_pos"
app:layout_constraintStart_toStartOf="@+id/modes_btn_neg"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/modes_recycler"
style="@style/Recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="4dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/modes_btn_neg"
app:layout_constraintEnd_toEndOf="@+id/modes_btn_pos"
app:layout_constraintStart_toStartOf="@+id/modes_btn_neg"
app:layout_constraintTop_toBottomOf="@+id/modes_title" />
<Button
android:id="@+id/modes_btn_neg"
style="@style/NormalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:text="@string/btn_cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/modes_btn_pos"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toEndOf="@+id/modes_btn_pos"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/modes_btn_pos"
style="@style/NormalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/btn_accept"
app:layout_constraintBaseline_toBaselineOf="@+id/modes_btn_neg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/modes_btn_neg" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
+1 -1
View File
@@ -76,7 +76,7 @@
</com.google.android.material.bottomappbar.BottomAppBar>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/entries_accept_btn"
android:id="@+id/entries_fab"
style="@style/FloatingActionButton"
android:src="@drawable/ic_done"
app:layout_anchor="@id/entries_bottom_bar" />
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mode_card"
style="@style/RecyclerItemCard">
<CheckBox
android:id="@+id/mode_checkbox"
android:layout_width="match_parent"
android:layout_height="41dp"
android:layout_marginStart="4dp"
android:background="@color/surfaceCard"
android:ellipsize="end"
android:lines="1"
android:padding="4dp"
android:textColor="@color/themeLight"
android:textSize="@dimen/text_size_medium" />
</androidx.cardview.widget.CardView>
@@ -7,7 +7,11 @@
<fragment
android:id="@+id/nav_entries"
android:name="com.rtbishop.look4sat.presentation.entriesScreen.EntriesFragment"
tools:layout="@layout/fragment_entries" />
tools:layout="@layout/fragment_entries">
<action
android:id="@+id/action_entries_to_modes"
app:destination="@id/nav_modes" />
</fragment>
<fragment
android:id="@+id/nav_passes"
android:name="com.rtbishop.look4sat.presentation.passesScreen.PassesFragment"
@@ -54,6 +58,10 @@
android:id="@+id/nav_sources"
android:name="com.rtbishop.look4sat.presentation.settingsScreen.SourcesDialog"
tools:layout="@layout/dialog_sources" />
<dialog
android:id="@+id/nav_modes"
android:name="com.rtbishop.look4sat.presentation.entriesScreen.ModesDialog"
tools:layout="@layout/dialog_modes" />
<action
android:id="@+id/action_global_entriesFragment"
app:destination="@id/nav_entries" />