Channel scan select (#1141)

pull/1158/head
Robert-0410 2024-07-28 02:58:41 -07:00 zatwierdzone przez GitHub
rodzic 81297c46e9
commit ed17ae0734
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
2 zmienionych plików z 176 dodań i 2 usunięć

Wyświetl plik

@ -16,12 +16,14 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ButtonDefaults import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card import androidx.compose.material.Card
import androidx.compose.material.Checkbox import androidx.compose.material.Checkbox
import androidx.compose.material.Chip import androidx.compose.material.Chip
@ -41,6 +43,9 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Check import androidx.compose.material.icons.twotone.Check
import androidx.compose.material.icons.twotone.Close import androidx.compose.material.icons.twotone.Close
import androidx.compose.material.Button
import androidx.compose.material.Surface
import androidx.compose.material.ButtonDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
@ -72,6 +77,8 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@ -180,9 +187,12 @@ fun ChannelScreen(
val channelUrl = channelSet.getChannelUrl() val channelUrl = channelSet.getChannelUrl()
val modemPresetName = Channel(loraConfig = channelSet.loraConfig).name val modemPresetName = Channel(loraConfig = channelSet.loraConfig).name
val userScannedQrCode = remember { mutableStateOf(false) }
val scannedQR = remember { mutableStateOf("") }
val barcodeLauncher = rememberLauncherForActivityResult(ScanContract()) { result -> val barcodeLauncher = rememberLauncherForActivityResult(ScanContract()) { result ->
if (result.contents != null) { if (result.contents != null) {
viewModel.setRequestChannelUrl(Uri.parse(result.contents)) scannedQR.value = result.contents
userScannedQrCode.value = true
} }
} }
@ -293,6 +303,15 @@ fun ChannelScreen(
.show() .show()
} }
if (userScannedQrCode.value)
/* Prompt the user to modify channels after scanning a QR code. */
ScannedQrCodeDialog(
channels = channels,
incoming = Uri.parse(scannedQR.value).toChannelSet(),
onDismiss = { userScannedQrCode.value = false },
onConfirm = { newChannelSet -> installSettings(newChannelSet) }
)
var showEditChannelDialog: Int? by remember { mutableStateOf(null) } var showEditChannelDialog: Int? by remember { mutableStateOf(null) }
if (showEditChannelDialog != null) { if (showEditChannelDialog != null) {
@ -547,6 +566,158 @@ private fun ChannelSelection(
} }
} }
/**
* Enables the user to select which channels to accept after scanning a QR code.
*/
@Suppress("LongMethod")
@Composable
fun ScannedQrCodeDialog(
channels: AppOnlyProtos.ChannelSet,
incoming: AppOnlyProtos.ChannelSet,
onDismiss: () -> Unit,
onConfirm: (AppOnlyProtos.ChannelSet) -> Unit
) {
var currentChannelSet by remember(channels) { mutableStateOf(channels) }
val modemPresetName = Channel(loraConfig = currentChannelSet.loraConfig).name
/* Holds selections made by the user */
val channelSelections = remember { mutableStateListOf(elements = Array(size = 8, 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++
}
Dialog(
onDismissRequest = { onDismiss() },
properties = DialogProperties(usePlatformDefaultWidth = false, dismissOnBackPress = true)
) {
Surface(
modifier = Modifier.fillMaxSize(),
shape = RoundedCornerShape(16.dp),
color = MaterialTheme.colors.background
) {
LazyColumn(
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)
)
}
itemsIndexed(incoming.settingsList) { index, channel ->
ChannelSelection(
index = index,
title = channel.name.ifEmpty { modemPresetName },
enabled = true,
isSelected = channelSelections[index],
onSelected = { 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)
}
}
)
}
/* User Actions via buttons */
item {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
/* Cancel */
Button(
onClick = onDismiss,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
.weight(1f)
.padding(3.dp)
) {
Text(
style = MaterialTheme.typography.body1,
text = stringResource(id = R.string.cancel)
)
}
/* Add - Appends incoming selected channels to the current set */
Button(
enabled = totalCount <= 8,
onClick = {
val appended = incoming.copy {
val result = settings.filterIndexed { i, _ ->
channelSelections.getOrNull(i) == true
}
settings.clear()
settings.addAll(currentChannelSet.settingsList)
settings.addAll(result)
}
onDismiss.invoke()
onConfirm.invoke(appended)
},
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
.weight(1f)
.padding(3.dp)
) {
Text(
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)
)
}
}
}
}
}
}
}
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun ChannelScreenPreview() { private fun ChannelScreenPreview() {

Wyświetl plik

@ -206,4 +206,7 @@
<string name="mute_8_hours">8 hours</string> <string name="mute_8_hours">8 hours</string>
<string name="mute_1_week">1 week</string> <string name="mute_1_week">1 week</string>
<string name="mute_always">Always</string> <string name="mute_always">Always</string>
<string name="scanned_channels">Scanned Channels</string>
<string name="current_channels">Current Channels</string>
<string name="replace">Replace</string>
</resources> </resources>