Refactor: Improve network device selection UI and logic (#2268)

pull/2265/head^2
James Rich 2025-06-27 11:29:27 +00:00 zatwierdzone przez GitHub
rodzic d709b2d2d0
commit 53cd893df1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
2 zmienionych plików z 65 dodań i 35 usunięć

Wyświetl plik

@ -359,6 +359,11 @@ fun ConnectionsScreen(
}
var selectedDeviceType by remember { mutableStateOf(DeviceType.BLE) }
LaunchedEffect(selectedDevice) {
// Determine based on the selected device - if disconnected, keep the last selected type
selectedDeviceType =
selectedDevice.let { DeviceType.fromAddress(it) } ?: selectedDeviceType
}
SingleChoiceSegmentedButtonRow(
modifier = Modifier.fillMaxWidth(),
) {
@ -709,7 +714,25 @@ private tailrec fun Context.findActivity(): Activity = when (this) {
private enum class DeviceType {
BLE,
TCP,
USB,
USB;
companion object {
fun fromAddress(address: String): DeviceType? {
val prefix = address[0]
val isBLE: Boolean = prefix == 'x'
val isUSB: Boolean = prefix == 's'
val isTCP: Boolean = prefix == 't'
val isMock: Boolean = prefix == 'm'
val isDisconnect: Boolean = prefix == 'n'
return when {
isBLE -> BLE
isUSB -> USB
isTCP -> TCP
isMock -> USB // Treat mock as USB for UI purposes
else -> null
}
}
}
}
private const val SCAN_PERIOD: Long = 10000 // 10 seconds

Wyświetl plik

@ -17,28 +17,28 @@
package com.geeksville.mesh.ui.connections.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.WifiFind
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
@ -47,6 +47,7 @@ import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.repository.network.NetworkRepository
import com.geeksville.mesh.ui.connections.isIPAddress
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("MagicNumber", "LongMethod")
@Composable
fun NetworkDevices(
@ -86,56 +87,62 @@ fun NetworkDevices(
)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.selectable(
selected = ("t$manualIpAddress:$manualIpPort" == selectedDevice),
onClick = {
if (manualIpAddress.text.toString().isIPAddress()) {
scanModel.onSelected(
BTScanModel.DeviceListEntry(
"",
"t$manualIpAddress:$manualIpPort",
true
)
)
}
},
enabled = manualIpAddress.text.toString().isIPAddress(),
role = Role.Companion.RadioButton
)
.padding(8.dp),
verticalAlignment = Alignment.Companion.CenterVertically
verticalAlignment = Alignment.Companion.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
) {
RadioButton(
selected = ("t$manualIpAddress:$manualIpPort" == selectedDevice),
onClick = null,
enabled = manualIpAddress.toString().isIPAddress()
)
OutlinedTextField(
state = manualIpAddress,
lineLimits = TextFieldLineLimits.SingleLine,
label = { Text(stringResource(R.string.ip_address)) },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Companion.Number,
imeAction = ImeAction.Done
keyboardType = KeyboardType.Companion.Decimal,
imeAction = ImeAction.Next
),
modifier = Modifier
.weight(0.7f)
.padding(start = 16.dp)
modifier = Modifier.weight(.7f, fill = false) // Fill 70% of the space
)
OutlinedTextField(
state = manualIpPort,
placeholder = { Text(NetworkRepository.SERVICE_PORT.toString()) },
lineLimits = TextFieldLineLimits.SingleLine,
label = { Text(stringResource(R.string.ip_port)) },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Companion.Number,
keyboardType = KeyboardType.Companion.Decimal,
imeAction = ImeAction.Done
),
modifier = Modifier
.weight(weight = 0.3f)
.padding(start = 8.dp)
modifier = Modifier.weight(.3f, fill = false) // Fill remaining space
)
IconButton(
onClick = {
if (manualIpAddress.text.toString().isIPAddress()) {
val fullAddress =
"t" + if (
manualIpPort.text.isNotEmpty() &&
manualIpPort.text.toString().toInt() != NetworkRepository.SERVICE_PORT
) {
"${manualIpAddress.text}:${manualIpPort.text}"
} else {
"${manualIpAddress.text}"
}
scanModel.onSelected(
BTScanModel.DeviceListEntry(
"${manualIpAddress.text}",
fullAddress,
true
)
)
}
}
) {
Icon(
imageVector = Icons.Default.WifiFind,
contentDescription = stringResource(R.string.add),
modifier = Modifier.size(32.dp)
)
}
}
}