kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
				
				
				
			Refactor: Improve network device selection UI and logic (#2268)
							rodzic
							
								
									d709b2d2d0
								
							
						
					
					
						commit
						53cd893df1
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue