diff --git a/app/src/androidTest/java/com/geeksville/mesh/compose/ScannedQrCodeDialogTest.kt b/app/src/androidTest/java/com/geeksville/mesh/compose/ScannedQrCodeDialogTest.kt
index b9f40878e..4219d6561 100644
--- a/app/src/androidTest/java/com/geeksville/mesh/compose/ScannedQrCodeDialogTest.kt
+++ b/app/src/androidTest/java/com/geeksville/mesh/compose/ScannedQrCodeDialogTest.kt
@@ -68,16 +68,37 @@ class ScannedQrCodeDialogTest {
}
@Test
- fun testScannedQrCodeDialog_showsCancelAddAndReplaceButtons() {
+ fun testScannedQrCodeDialog_showsDialogTitle() {
composeTestRule.apply {
testScannedQrCodeDialog()
- onNodeWithText(getString(R.string.cancel)).assertIsDisplayed()
+ // Verify that the dialog title is displayed
+ onNodeWithText(getString(R.string.new_channel_rcvd)).assertIsDisplayed()
+ }
+ }
+
+ @Test
+ fun testScannedQrCodeDialog_showsAddAndReplaceButtons() {
+ composeTestRule.apply {
+ testScannedQrCodeDialog()
+
+ // Verify that the "Add" and "Replace" buttons are displayed
onNodeWithText(getString(R.string.add)).assertIsDisplayed()
onNodeWithText(getString(R.string.replace)).assertIsDisplayed()
}
}
+ @Test
+ fun testScannedQrCodeDialog_showsCancelAndAcceptButtons() {
+ composeTestRule.apply {
+ testScannedQrCodeDialog()
+
+ // Verify the "Cancel" and "Accept" buttons are displayed
+ onNodeWithText(getString(R.string.cancel)).assertIsDisplayed()
+ onNodeWithText(getString(R.string.accept)).assertIsDisplayed()
+ }
+ }
+
@Test
fun testScannedQrCodeDialog_clickCancelButton() {
var onDismissClicked = false
@@ -98,8 +119,8 @@ class ScannedQrCodeDialogTest {
composeTestRule.apply {
testScannedQrCodeDialog(onConfirm = { actualChannelSet = it })
- // Click the "Replace" button
- onNodeWithText(getString(R.string.replace)).performClick()
+ // Click the "Accept" button
+ onNodeWithText(getString(R.string.accept)).performClick()
}
// Verify onConfirm is called with the correct ChannelSet
@@ -112,13 +133,16 @@ class ScannedQrCodeDialogTest {
composeTestRule.apply {
testScannedQrCodeDialog(onConfirm = { actualChannelSet = it })
- // Click the "Add" button
+ // Click the "Add" button then the "Accept" button
onNodeWithText(getString(R.string.add)).performClick()
+ onNodeWithText(getString(R.string.accept)).performClick()
}
// Verify onConfirm is called with the correct ChannelSet
val expectedChannelSet = channels.copy {
- settings.addAll(incoming.settingsList)
+ val list = LinkedHashSet(settings + incoming.settingsList)
+ settings.clear()
+ settings.addAll(list)
}
Assert.assertEquals(expectedChannelSet, actualChannelSet)
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
index 0387e0fae..1810b6ef6 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
@@ -525,9 +525,12 @@ private fun ChannelListView(
)
}
OutlinedButton(
- modifier = Modifier.fillMaxWidth(),
onClick = onClick,
+ modifier = Modifier.fillMaxWidth(),
enabled = enabled,
+ colors = ButtonDefaults.outlinedButtonColors(
+ contentColor = MaterialTheme.colors.onSurface,
+ ),
) { Text(text = stringResource(R.string.edit)) }
},
second = {
diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt b/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt
index c475759c3..7fcc173c9 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/components/ScannedQrCodeDialog.kt
@@ -1,19 +1,23 @@
package com.geeksville.mesh.ui.components
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedButton
import androidx.compose.material.Surface
import androidx.compose.material.Text
+import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
@@ -23,6 +27,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@@ -32,12 +37,12 @@ import com.geeksville.mesh.R
import com.geeksville.mesh.channelSet
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.Channel
-import com.geeksville.mesh.ui.components.config.ChannelCard
import com.geeksville.mesh.ui.components.config.ChannelSelection
/**
* Enables the user to select which channels to accept after scanning a QR code.
*/
+@OptIn(ExperimentalLayoutApi::class)
@Suppress("LongMethod")
@Composable
fun ScannedQrCodeDialog(
@@ -46,19 +51,31 @@ fun ScannedQrCodeDialog(
onDismiss: () -> Unit,
onConfirm: (ChannelSet) -> Unit
) {
- var currentChannelSet by remember(channels) { mutableStateOf(channels) }
- val modemPresetName = Channel(loraConfig = currentChannelSet.loraConfig).name
+ var shouldReplace by remember { mutableStateOf(true) }
+
+ val channelSet = remember(shouldReplace) {
+ if (shouldReplace) {
+ incoming
+ } else {
+ channels.copy {
+ val result = LinkedHashSet(settings + incoming.settingsList)
+ settings.clear()
+ settings.addAll(result)
+ }
+ }
+ }
+
+ val modemPresetName = Channel(loraConfig = channelSet.loraConfig).name
/* Holds selections made by the user */
- val channelSelections = remember { mutableStateListOf(elements = Array(size = 8, init = { true })) }
+ val channelSelections = remember(channelSet) {
+ mutableStateListOf(elements = Array(size = channelSet.settingsCount, init = { true }))
+ }
- /* The save button is enabled based on this count */
- var totalCount = currentChannelSet.settingsList.size
- for ((index, isSelected) in channelSelections.withIndex()) {
- if (index >= incoming.settingsList.size)
- break
- if (isSelected)
- totalCount++
+ val selectedChannelSet = channelSet.copy {
+ val result = settings.filterIndexed { i, _ -> channelSelections.getOrNull(i) == true }
+ settings.clear()
+ settings.addAll(result)
}
Dialog(
@@ -66,7 +83,7 @@ fun ScannedQrCodeDialog(
properties = DialogProperties(usePlatformDefaultWidth = false, dismissOnBackPress = true)
) {
Surface(
- modifier = Modifier.fillMaxSize(),
+ modifier = Modifier.widthIn(max = 600.dp),
shape = RoundedCornerShape(16.dp),
color = MaterialTheme.colors.background
) {
@@ -74,108 +91,89 @@ fun ScannedQrCodeDialog(
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
- /* Incoming ChannelSet */
item {
Text(
- style = MaterialTheme.typography.body1,
- text = stringResource(id = R.string.scanned_channels)
+ text = stringResource(id = R.string.new_channel_rcvd),
+ modifier = Modifier.padding(20.dp),
+ style = MaterialTheme.typography.h6,
)
}
- itemsIndexed(incoming.settingsList) { index, channel ->
+ itemsIndexed(channelSet.settingsList) { index, channel ->
ChannelSelection(
index = index,
title = channel.name.ifEmpty { modemPresetName },
enabled = true,
isSelected = channelSelections[index],
- onSelected = { channelSelections[index] = it }
+ onSelected = {
+ if (it || selectedChannelSet.settingsCount > 1) {
+ channelSelections[index] = it
+ }
+ },
)
}
- /* Current ChannelSet */
item {
- Text(
- style = MaterialTheme.typography.body1,
- text = stringResource(id = R.string.current_channels)
- )
- }
- itemsIndexed(currentChannelSet.settingsList) { index, channel ->
- ChannelCard(
- index = index,
- title = channel.name.ifEmpty { modemPresetName },
- enabled = true,
- onEditClick = { /* Currently we don't enable editing from this dialog. */ },
- onDeleteClick = {
- val list = currentChannelSet.settingsList.toMutableList()
- list.removeAt(index)
- currentChannelSet = currentChannelSet.copy {
- settings.clear()
- settings.addAll(list)
- }
- }
- )
+ Row(
+ modifier = Modifier.padding(vertical = 20.dp),
+ ) {
+ val selectedColors = ButtonDefaults.buttonColors()
+ val unselectedColors = ButtonDefaults.outlinedButtonColors(
+ contentColor = MaterialTheme.colors.onSurface,
+ )
+
+ OutlinedButton(
+ onClick = { shouldReplace = false },
+ modifier = Modifier
+ .height(48.dp)
+ .weight(1f),
+ colors = if (!shouldReplace) selectedColors else unselectedColors,
+ ) { Text(text = stringResource(R.string.add)) }
+
+ OutlinedButton(
+ onClick = { shouldReplace = true },
+ modifier = Modifier
+ .height(48.dp)
+ .weight(1f),
+ colors = if (shouldReplace) selectedColors else unselectedColors,
+ ) { Text(text = stringResource(R.string.replace)) }
+ }
}
/* User Actions via buttons */
item {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceEvenly
+ FlowRow(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 24.dp)
) {
- /* Cancel */
- Button(
- onClick = onDismiss,
- modifier = Modifier
- .fillMaxWidth()
- .height(48.dp)
- .weight(1f)
- .padding(3.dp)
+ TextButton(
+ onClick = {
+ onDismiss()
+ },
) {
Text(
+ text = stringResource(id = R.string.cancel),
+ color = MaterialTheme.colors.onSurface,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
style = MaterialTheme.typography.body1,
- text = stringResource(id = R.string.cancel)
)
}
- /* Add - Appends incoming selected channels to the current set */
- Button(
- enabled = totalCount <= 8,
+ TextButton(
onClick = {
- val appended = currentChannelSet.copy {
- val result = incoming.settingsList.filterIndexed { i, _ ->
- channelSelections.getOrNull(i) == true
- }
- settings.addAll(result)
- }
- onDismiss.invoke()
- onConfirm.invoke(appended)
+ onDismiss()
+ onConfirm(selectedChannelSet)
},
- modifier = Modifier
- .fillMaxWidth()
- .height(48.dp)
- .weight(1f)
- .padding(3.dp)
+ enabled = selectedChannelSet.settingsCount in 1..8,
) {
Text(
+ text = stringResource(id = R.string.accept),
+ color = MaterialTheme.colors.onSurface,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
style = MaterialTheme.typography.body1,
- text = stringResource(id = R.string.add)
- )
- }
-
- /* Replace - Replaces the previous set with the scanned channel set */
- Button(
- onClick = {
- onDismiss.invoke()
- onConfirm.invoke(incoming)
- },
- modifier = Modifier
- .fillMaxWidth()
- .height(48.dp)
- .weight(1f)
- .padding(3.dp)
- ) {
- Text(
- style = MaterialTheme.typography.body1,
- text = stringResource(id = R.string.replace)
)
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/config/ChannelSettingsItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/components/config/ChannelSettingsItemList.kt
index cec29c0a6..1e999fe29 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/components/config/ChannelSettingsItemList.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/components/config/ChannelSettingsItemList.kt
@@ -42,6 +42,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -79,11 +80,18 @@ private fun ChannelItem(
val textColor = if (enabled) Color.Unspecified
else MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
- Chip(onClick = onClick) { Text("$index") }
+ Chip(onClick = onClick) {
+ Text(
+ text = "$index",
+ color = textColor,
+ )
+ }
Text(
text = title,
modifier = Modifier.weight(1f),
color = textColor,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
style = MaterialTheme.typography.body1,
)
content()
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f889f67c6..83970e728 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -211,7 +211,5 @@
- Do you want to add a new channel?
- Do you want to add %d new channels?
- Scanned Channels
- Current Channels
Replace