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.os.Bundle
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
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.databinding.FragmentEntriesBinding import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
import com.rtbishop.look4sat.domain.model.DataState import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatItem import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.presentation.getNavResult
import com.rtbishop.look4sat.presentation.setNavResult import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -59,13 +60,19 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
this.layoutManager = layoutManager this.layoutManager = layoutManager
addItemDecoration(itemDecoration) addItemDecoration(itemDecoration)
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false (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() } entriesBackBtn.setOnClickListener { findNavController().navigateUp() }
entriesSearch.doOnTextChanged { text, _, _, _ -> viewModel.setQuery(text.toString()) } entriesSearch.doOnTextChanged { text, _, _, _ -> viewModel.setQuery(text.toString()) }
entriesModesBtn.setOnClickListener { showModesDialog() } entriesModesBtn.setOnClickListener { navigateToModesDialog() }
entriesSelectBtn.setOnClickListener { viewModel.selectCurrentItems(true) } entriesSelectBtn.setOnClickListener { viewModel.selectCurrentItems(true) }
entriesClearBtn.setOnClickListener { viewModel.selectCurrentItems(false) } entriesClearBtn.setOnClickListener { viewModel.selectCurrentItems(false) }
entriesAcceptBtn.setOnClickListener { entriesFab.setOnClickListener {
setNavResult("selection", viewModel.saveSelection()) setNavResult("selection", viewModel.saveSelection())
findNavController().popBackStack() findNavController().popBackStack()
} }
@@ -73,6 +80,9 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
viewModel.satData.observe(viewLifecycleOwner) { satData -> viewModel.satData.observe(viewLifecycleOwner) { satData ->
handleSatData(satData, binding, adapter) handleSatData(satData, binding, adapter)
} }
getNavResult<List<String>>(R.id.nav_entries, "modes") { modes ->
viewModel.saveSelectedModes(modes)
}
} }
private fun handleSatData( private fun handleSatData(
@@ -100,32 +110,8 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) {
} }
} }
private fun showModesDialog() { private fun navigateToModesDialog() {
val modes = arrayOf( val direction = EntriesFragmentDirections.actionEntriesToModes()
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3", findNavController().navigate(direction)
"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()
}
} }
} }
@@ -41,35 +41,26 @@ class EntriesViewModel @Inject constructor(
private val itemsWithQuery = currentQuery.switchMap { query -> private val itemsWithQuery = currentQuery.switchMap { query ->
itemsWithModes.map { items -> filterByQuery(items, query) } itemsWithModes.map { items -> filterByQuery(items, query) }
} }
// private var shouldSelectAll = true
val satData = itemsWithQuery.map { items -> DataState.Success(items) } val satData = itemsWithQuery.map { items -> DataState.Success(items) }
fun selectCurrentItems(selectAll: Boolean) { fun selectCurrentItems(selectAll: Boolean) {
itemsWithQuery.value?.let { itemsWithQuery -> itemsWithQuery.value?.let { itemsWithQuery ->
updateSelection(itemsWithQuery.map { item -> item.catnum }, selectAll) updateSelection(itemsWithQuery.map { item -> item.catnum }, selectAll)
// shouldSelectAll = shouldSelectAll.not()
} }
} }
fun loadSelectedModes(): List<String> {
return preferences.loadModesSelection()
}
fun saveSelectedModes(modes: List<String>) { fun saveSelectedModes(modes: List<String>) {
transModes.value = modes transModes.value = modes
preferences.saveModesSelection(modes) preferences.saveModesSelection(modes)
} }
fun setQuery(query: String) { fun setQuery(query: String) {
// shouldSelectAll = true
currentQuery.value = query currentQuery.value = query
} }
fun saveSelection(): List<Int> { fun saveSelection(): List<Int> {
return itemsFromRepo.value?.let { itemsAll -> return itemsFromRepo.value?.let { itemsAll ->
val filteredItems = itemsAll.filter { item -> item.isSelected } itemsAll.filter { item -> item.isSelected }.map { item -> item.catnum }
// repository.setEntriesSelection(filteredItems.map { item -> item.catnum })
filteredItems.map { item -> item.catnum }
} ?: emptyList() } ?: 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.bottomappbar.BottomAppBar>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/entries_accept_btn" android:id="@+id/entries_fab"
style="@style/FloatingActionButton" style="@style/FloatingActionButton"
android:src="@drawable/ic_done" android:src="@drawable/ic_done"
app:layout_anchor="@id/entries_bottom_bar" /> 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 <fragment
android:id="@+id/nav_entries" android:id="@+id/nav_entries"
android:name="com.rtbishop.look4sat.presentation.entriesScreen.EntriesFragment" 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 <fragment
android:id="@+id/nav_passes" android:id="@+id/nav_passes"
android:name="com.rtbishop.look4sat.presentation.passesScreen.PassesFragment" android:name="com.rtbishop.look4sat.presentation.passesScreen.PassesFragment"
@@ -54,6 +58,10 @@
android:id="@+id/nav_sources" android:id="@+id/nav_sources"
android:name="com.rtbishop.look4sat.presentation.settingsScreen.SourcesDialog" android:name="com.rtbishop.look4sat.presentation.settingsScreen.SourcesDialog"
tools:layout="@layout/dialog_sources" /> 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 <action
android:id="@+id/action_global_entriesFragment" android:id="@+id/action_global_entriesFragment"
app:destination="@id/nav_entries" /> app:destination="@id/nav_entries" />