Implemented ModesDialog in Jetpack Compose

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:32 +00:00
1 parent c592b8d3ce
commit 39959c7a10
7 files changed
+89 -256

No files matched your search

-5
View File
@@ -107,11 +107,6 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
androidTestImplementation "androidx.test:core:$androidx_test_version"
androidTestImplementation "androidx.test:rules:$androidx_test_version"
androidTestImplementation "androidx.test:runner:$androidx_test_version"
androidTestImplementation "androidx.test.ext:junit:$androidx_junit_version"
androidTestImplementation "androidx.test.ext:truth:$androidx_test_version"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
androidTestImplementation "org.mockito:mockito-android:$mockito_version"
}
@@ -1,79 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
*/
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)
}
}
@@ -1,88 +0,0 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2022 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 3 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, see <https://www.gnu.org/licenses/>.
*/
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.databinding.DialogModesBinding
import com.rtbishop.look4sat.domain.ISettingsManager
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class ModesDialog : AppCompatDialogFragment(), ModesAdapter.ModesClickListener {
@Inject
lateinit var preferences: ISettingsManager
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)
}
}
}
@@ -0,0 +1,88 @@
package com.rtbishop.look4sat.presentation.passesScreen
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.rtbishop.look4sat.presentation.MainTheme
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>("AFSK", "AFSK S-Net")
@Preview(showBackground = true)
@Composable
private fun ModesDialogPreview() {
val modesList = remember { mutableStateOf(selectedModes) }
MainTheme {
ModesDialog(allModes, modesList.value, {}) {
if (modesList.value.contains(it)) modesList.value.remove(it)
else modesList.value.add(it)
}
}
}
@Composable
fun ModesDialog(
list: List<String>,
selected: List<String>,
toggle: () -> Unit,
click: (String) -> Unit
) {
Dialog(onDismissRequest = { toggle() }) {
ElevatedCard(modifier = Modifier.fillMaxHeight(0.9f)) {
LazyVerticalGrid(
columns = GridCells.Fixed(1),
modifier = Modifier.background(MaterialTheme.colorScheme.background),
horizontalArrangement = Arrangement.spacedBy(1.dp),
verticalArrangement = Arrangement.spacedBy(1.dp)
) {
items(list) { mode ->
Surface {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.fillMaxWidth()
.clickable {
click(mode)
toggle()
}) {
Text(
text = mode,
modifier = Modifier
.padding(start = 8.dp, end = 6.dp)
.weight(1f),
fontWeight = FontWeight.Normal,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Checkbox(
checked = selected.contains(mode),
onCheckedChange = null,
modifier = Modifier.padding(6.dp)
)
}
}
}
}
}
}
}
-66
View File
@@ -1,66 +0,0 @@
<?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/modes_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="6dp"
android:layout_marginBottom="6dp"
android:text="@string/btn_cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/modes_btn_pos"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="spread_inside"
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="6dp"
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>
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/RecyclerItemCard">
<CheckBox
android:id="@+id/mode_checkbox"
android:layout_width="match_parent"
android:layout_height="41dp"
android:layout_marginStart="6dp"
android:background="@color/cardRegular"
android:ellipsize="end"
android:lines="1"
android:padding="4dp"
android:textColor="@color/textMain"
android:textSize="@dimen/text_size_medium" />
</androidx.cardview.widget.CardView>
+1 -1
View File
@@ -2,7 +2,7 @@ buildscript {
ext {
hilt_version = '2.45'
hilt_compose_version = '1.0.0'
application_version = '7.4.1'
application_version = '7.4.2'
library_version = '7.1.2'
kotlin_android_version = '1.8.10'
core_ktx_version = '1.9.0'