mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
allow deleting of recent nodes, use long name (#2456)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
5e5fc19fc0
commit
be30757720
7 changed files with 119 additions and 30 deletions
|
|
@ -127,8 +127,8 @@ class BTScanModel @Inject constructor(
|
|||
}
|
||||
|
||||
// Include saved IP connections
|
||||
recent.forEach { address ->
|
||||
addDevice(DeviceListEntry(context.getString(R.string.meshtastic), address, true))
|
||||
recent.forEach { (address, name) ->
|
||||
addDevice(DeviceListEntry(name, address, true))
|
||||
}
|
||||
|
||||
usb.forEach { (_, d) ->
|
||||
|
|
@ -295,27 +295,58 @@ class BTScanModel @Inject constructor(
|
|||
}.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun getRecentAddresses(): List<String> {
|
||||
private fun getRecentAddresses(): List<Pair<String, String>> {
|
||||
val jsonAddresses = preferences.getString("recent-ip-addresses", "[]") ?: "[]"
|
||||
val listAddresses = JSONArray(jsonAddresses).let { jsonArray ->
|
||||
List(jsonArray.length()) { index -> jsonArray.getString(index) }
|
||||
val jsonArray = JSONArray(jsonAddresses)
|
||||
val listAddresses = mutableListOf<Pair<String, String>>()
|
||||
var needsMigration = false
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val item = jsonArray.get(i)
|
||||
if (item is org.json.JSONObject) {
|
||||
val address = item.getString("address")
|
||||
val name = item.getString("name")
|
||||
listAddresses.add(address to name)
|
||||
} else if (item is String) {
|
||||
// Old format: just the address, use default name and mark for migration
|
||||
listAddresses.add(item to context.getString(R.string.meshtastic))
|
||||
needsMigration = true
|
||||
}
|
||||
}
|
||||
// If migration is needed, rewrite the storage in the new format
|
||||
if (needsMigration) {
|
||||
setRecentAddresses(listAddresses)
|
||||
}
|
||||
return listAddresses
|
||||
}
|
||||
|
||||
private fun setRecentAddresses(addresses: List<String>) {
|
||||
private fun setRecentAddresses(addresses: List<Pair<String, String>>) {
|
||||
val jsonArray = JSONArray()
|
||||
addresses.forEach { (address, name) ->
|
||||
val obj = org.json.JSONObject()
|
||||
obj.put("address", address)
|
||||
obj.put("name", name)
|
||||
jsonArray.put(obj)
|
||||
}
|
||||
preferences.edit {
|
||||
putString("recent-ip-addresses", addresses.toString())
|
||||
putString("recent-ip-addresses", jsonArray.toString())
|
||||
}
|
||||
recentIpAddresses.value = addresses
|
||||
}
|
||||
|
||||
fun addRecentAddress(address: String) {
|
||||
// Remove 'name' parameter from addRecentAddress and related logic
|
||||
fun addRecentAddress(address: String, overrideName: String? = null) {
|
||||
if (!address.startsWith("t")) return
|
||||
val existingItems = getRecentAddresses()
|
||||
val updatedList = mutableListOf<String>()
|
||||
updatedList.add(address)
|
||||
updatedList.addAll(existingItems.filter { it != address }.take(2))
|
||||
val updatedList = mutableListOf<Pair<String, String>>()
|
||||
val displayName = overrideName ?: context.getString(R.string.meshtastic)
|
||||
updatedList.add(address to displayName)
|
||||
updatedList.addAll(existingItems.filter { it.first != address }.take(2))
|
||||
setRecentAddresses(updatedList)
|
||||
}
|
||||
|
||||
fun removeRecentAddress(address: String) {
|
||||
val existingItems = getRecentAddresses()
|
||||
val updatedList = existingItems.filter { it.first != address }
|
||||
setRecentAddresses(updatedList)
|
||||
}
|
||||
|
||||
|
|
@ -324,7 +355,7 @@ class BTScanModel @Inject constructor(
|
|||
fun onSelected(it: DeviceListEntry): Boolean {
|
||||
// If the device is paired, let user select it, otherwise start the pairing flow
|
||||
if (it.bonded) {
|
||||
addRecentAddress(it.fullAddress)
|
||||
addRecentAddress(it.fullAddress, connectedNodeLongName)
|
||||
changeDeviceAddress(it.fullAddress)
|
||||
return true
|
||||
} else {
|
||||
|
|
@ -345,6 +376,9 @@ class BTScanModel @Inject constructor(
|
|||
|
||||
private val _spinner = MutableStateFlow(false)
|
||||
val spinner: StateFlow<Boolean> get() = _spinner.asStateFlow()
|
||||
|
||||
// Add a new property to hold the connected node's long name
|
||||
var connectedNodeLongName: String? = null
|
||||
}
|
||||
|
||||
const val NO_DEVICE_SELECTED = "n"
|
||||
|
|
|
|||
|
|
@ -278,6 +278,8 @@ fun ConnectionsScreen(
|
|||
|
||||
val isConnected by uiViewModel.isConnected.collectAsState(false)
|
||||
val ourNode by uiViewModel.ourNodeInfo.collectAsState()
|
||||
// Set the connected node long name for BTScanModel
|
||||
scanModel.connectedNodeLongName = ourNode?.user?.longName
|
||||
if (isConnected) {
|
||||
ourNode?.let { node ->
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -63,12 +63,12 @@ fun BLEDevices(
|
|||
)
|
||||
btDevices.forEach { device ->
|
||||
DeviceListItem(
|
||||
connectionState,
|
||||
device,
|
||||
device.fullAddress == selectedDevice
|
||||
) {
|
||||
scanModel.onSelected(device)
|
||||
}
|
||||
connectionState = connectionState,
|
||||
device = device,
|
||||
selected = device.fullAddress == selectedDevice,
|
||||
onSelect = { scanModel.onSelected(device) },
|
||||
modifier = Modifier
|
||||
)
|
||||
}
|
||||
if (isScanning) {
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ fun DeviceListItem(
|
|||
device: BTScanModel.DeviceListEntry,
|
||||
selected: Boolean,
|
||||
onSelect: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val icon = if (device.isBLE) {
|
||||
Icons.Default.Bluetooth
|
||||
|
|
@ -102,13 +103,18 @@ fun DeviceListItem(
|
|||
}
|
||||
}
|
||||
|
||||
val useSelectable = modifier == Modifier
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = selected,
|
||||
onClick = onSelect,
|
||||
),
|
||||
modifier = if (useSelectable) {
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = selected,
|
||||
onClick = onSelect,
|
||||
)
|
||||
} else {
|
||||
modifier.fillMaxWidth()
|
||||
},
|
||||
headlineContent = { Text(device.name) },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
|
|
|
|||
|
|
@ -47,6 +47,13 @@ import com.geeksville.mesh.model.BTScanModel
|
|||
import com.geeksville.mesh.repository.network.NetworkRepository
|
||||
import com.geeksville.mesh.service.MeshService
|
||||
import com.geeksville.mesh.ui.connections.isIPAddress
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.remember
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
|
|
@ -59,15 +66,50 @@ fun NetworkDevices(
|
|||
) {
|
||||
val manualIpAddress = rememberTextFieldState("")
|
||||
val manualIpPort = rememberTextFieldState(NetworkRepository.Companion.SERVICE_PORT.toString())
|
||||
var showDeleteDialog by remember { mutableStateOf(false) }
|
||||
var deviceToDelete by remember { mutableStateOf<BTScanModel.DeviceListEntry?>(null) }
|
||||
Text(
|
||||
text = stringResource(R.string.network),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
networkDevices.forEach { device ->
|
||||
DeviceListItem(connectionState, device, device.fullAddress == selectedDevice) {
|
||||
scanModel.onSelected(device)
|
||||
val isRecent = device.isTCP && device.fullAddress.startsWith("t")
|
||||
val modifier = if (isRecent) {
|
||||
Modifier.combinedClickable(
|
||||
onClick = { scanModel.onSelected(device) },
|
||||
onLongClick = {
|
||||
deviceToDelete = device
|
||||
showDeleteDialog = true
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
DeviceListItem(
|
||||
connectionState, device, device.fullAddress == selectedDevice, onSelect = { scanModel.onSelected(device) },
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
if (showDeleteDialog && deviceToDelete != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDeleteDialog = false },
|
||||
title = { Text(stringResource(R.string.delete)) },
|
||||
text = { Text(stringResource(R.string.confirm_delete_node)) },
|
||||
confirmButton = {
|
||||
Button(onClick = {
|
||||
scanModel.removeRecentAddress(deviceToDelete!!.fullAddress)
|
||||
showDeleteDialog = false
|
||||
}) {
|
||||
Text(stringResource(R.string.delete))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(onClick = { showDeleteDialog = false }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
if (networkDevices.filterNot { it.isDisconnect }.isEmpty()) {
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -48,9 +48,13 @@ fun UsbDevices(
|
|||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
usbDevices.forEach { device ->
|
||||
DeviceListItem(connectionState, device, device.fullAddress == selectedDevice) {
|
||||
scanModel.onSelected(device)
|
||||
}
|
||||
DeviceListItem(
|
||||
connectionState = connectionState,
|
||||
device = device,
|
||||
selected = device.fullAddress == selectedDevice,
|
||||
onSelect = { scanModel.onSelected(device) },
|
||||
modifier = Modifier
|
||||
)
|
||||
}
|
||||
if (usbDevices.filterNot { it.isDisconnect || it.isMock }.isEmpty()) {
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -780,6 +780,8 @@
|
|||
<string name="security_icon_help_show_less">Show Current Status</string>
|
||||
<string name="security_icon_help_dismiss">Dismiss</string>
|
||||
|
||||
<string name="confirm_delete_node">Are you sure you want to delete this node?</string>
|
||||
|
||||
<string name="replying_to">Replying to %1$s</string>
|
||||
<string name="cancel_reply">Cancel reply</string>
|
||||
<string name="delete_messages_title">Delete Messages?</string>
|
||||
|
|
@ -787,5 +789,4 @@
|
|||
<string name="message_input_label">Message</string>
|
||||
<string name="type_a_message">Type a message</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue