feat: add `fixed_position` to config import/export

pull/1302/head
andrekir 2024-10-09 19:41:05 -03:00 zatwierdzone przez Andre K
rodzic 0062d38c8b
commit aa84d47375
5 zmienionych plików z 64 dodań i 35 usunięć

Wyświetl plik

@ -9,6 +9,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
import com.geeksville.mesh.R
import com.geeksville.mesh.deviceProfile
import com.geeksville.mesh.position
import com.geeksville.mesh.ui.components.config.EditDeviceProfileDialog
import org.junit.Assert
import org.junit.Rule
@ -29,17 +30,22 @@ class EditDeviceProfileDialogTest {
longName = "Long name"
shortName = "Short name"
channelUrl = "https://meshtastic.org/e/#CgMSAQESBggBQANIAQ"
fixedPosition = position {
latitudeI = 327766650
longitudeI = -967969890
altitude = 138
}
}
private fun testEditDeviceProfileDialog(
onDismissRequest: () -> Unit = {},
onAddClick: (DeviceProfile) -> Unit = {},
onDismiss: () -> Unit = {},
onConfirm: (DeviceProfile) -> Unit = {},
) = composeTestRule.setContent {
EditDeviceProfileDialog(
title = title,
deviceProfile = deviceProfile,
onAddClick = onAddClick,
onDismissRequest = onDismissRequest,
onConfirm = onConfirm,
onDismiss = onDismiss,
)
}
@ -68,7 +74,7 @@ class EditDeviceProfileDialogTest {
fun testEditDeviceProfileDialog_clickCancelButton() {
var onDismissClicked = false
composeTestRule.apply {
testEditDeviceProfileDialog(onDismissRequest = { onDismissClicked = true })
testEditDeviceProfileDialog(onDismiss = { onDismissClicked = true })
// Click the "Cancel" button
onNodeWithText(getString(R.string.cancel)).performClick()
@ -82,7 +88,7 @@ class EditDeviceProfileDialogTest {
fun testEditDeviceProfileDialog_addChannels() {
var actualDeviceProfile: DeviceProfile? = null
composeTestRule.apply {
testEditDeviceProfileDialog(onAddClick = { actualDeviceProfile = it })
testEditDeviceProfileDialog(onConfirm = { actualDeviceProfile = it })
onNodeWithText(getString(R.string.save)).performClick()
}

Wyświetl plik

@ -372,7 +372,7 @@ class RadioConfigViewModel @Inject constructor(
.setShortName(if (hasShortName()) shortName else it.shortName)
.setIsLicensed(it.isLicensed)
.build()
if (it != user) setOwner(user)
setOwner(user)
}
if (hasChannelUrl()) try {
setChannels(channelUrl)
@ -389,6 +389,9 @@ class RadioConfigViewModel @Inject constructor(
setConfig(newConfig)
}
}
if (hasFixedPosition()) {
setFixedPosition(myNodeNum!!, Position(fixedPosition))
}
if (hasModuleConfig()) {
val descriptor = ModuleConfigProtos.ModuleConfig.getDescriptor()
moduleConfig.allFields.forEach { (field, value) ->

Wyświetl plik

@ -136,19 +136,22 @@ class RadioConfigRepository @Inject constructor(
* Flow representing the combined [DeviceProfile] protobuf.
*/
val deviceProfileFlow: Flow<DeviceProfile> = combine(
nodeDBbyNum,
nodeDB.ourNodeInfo,
channelSetFlow,
localConfigFlow,
moduleConfigFlow,
) { nodes, channels, localConfig, localModuleConfig ->
) { node, channels, localConfig, localModuleConfig ->
deviceProfile {
nodes.values.firstOrNull()?.user?.let {
node?.user?.let {
longName = it.longName
shortName = it.shortName
}
channelUrl = channels.getChannelUrl().toString()
config = localConfig
moduleConfig = localModuleConfig
if (node != null && localConfig.position.fixedPosition) {
fixedPosition = node.position
}
}
}

Wyświetl plik

@ -288,7 +288,7 @@ fun RadioConfigNavHost(
if (showEditDeviceProfileDialog) EditDeviceProfileDialog(
title = if (deviceProfile != null) "Import configuration" else "Export configuration",
deviceProfile = deviceProfile ?: viewModel.currentDeviceProfile,
onAddClick = {
onConfirm = {
showEditDeviceProfileDialog = false
if (deviceProfile != null) {
viewModel.installProfile(it)
@ -302,7 +302,7 @@ fun RadioConfigNavHost(
exportConfigLauncher.launch(intent)
}
},
onDismissRequest = {
onDismiss = {
showEditDeviceProfileDialog = false
viewModel.setDeviceProfile(null)
}

Wyświetl plik

@ -7,8 +7,11 @@ import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
@ -16,34 +19,51 @@ import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.ClientOnlyProtos
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
import com.geeksville.mesh.R
import com.geeksville.mesh.deviceProfile
import com.geeksville.mesh.ui.components.SwitchPreference
import com.google.protobuf.Descriptors
private const val SupportedFields = 7
@Suppress("LongMethod")
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun EditDeviceProfileDialog(
title: String,
deviceProfile: ClientOnlyProtos.DeviceProfile,
onAddClick: (ClientOnlyProtos.DeviceProfile) -> Unit,
onDismissRequest: () -> Unit,
deviceProfile: DeviceProfile,
onConfirm: (DeviceProfile) -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
val state = remember {
val fields = deviceProfile.descriptorForType.fields
.filter { it.number < SupportedFields } // TODO add ringtone & canned messages
mutableStateMapOf<Descriptors.FieldDescriptor, Boolean>()
.apply { putAll(fields.associateWith(deviceProfile::hasField)) }
}
AlertDialog(
title = { Text(title) },
onDismissRequest = onDismissRequest,
onDismissRequest = onDismiss,
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
text = {
Column(modifier.fillMaxWidth()) {
Text(
text = title,
style = MaterialTheme.typography.h6.copy(
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
)
Divider()
state.keys.sortedBy { it.number }.forEach { field ->
SwitchPreference(
title = field.name,
@ -53,33 +73,30 @@ fun EditDeviceProfileDialog(
padding = PaddingValues(0.dp)
)
}
Divider()
}
},
buttons = {
FlowRow(
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier
.fillMaxWidth()
.padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TextButton(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
.weight(1f),
onClick = onDismissRequest
modifier = modifier.weight(1f),
onClick = onDismiss
) { Text(stringResource(R.string.cancel)) }
Button(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
.weight(1f),
modifier = modifier.weight(1f),
onClick = {
val builder = ClientOnlyProtos.DeviceProfile.newBuilder()
val builder = DeviceProfile.newBuilder()
deviceProfile.allFields.forEach { (field, value) ->
if (state[field] == true) {
builder.setField(field, value)
}
}
onAddClick(builder.build())
onConfirm(builder.build())
},
enabled = state.values.any { it },
) { Text(stringResource(R.string.save)) }
@ -93,8 +110,8 @@ fun EditDeviceProfileDialog(
private fun EditDeviceProfileDialogPreview() {
EditDeviceProfileDialog(
title = "Export configuration",
deviceProfile = deviceProfile { },
onAddClick = { },
onDismissRequest = { },
deviceProfile = DeviceProfile.getDefaultInstance(),
onConfirm = {},
onDismiss = {},
)
}