mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: add feedback for configuration changes
This commit is contained in:
parent
2dd0e1f1e2
commit
7c30d86e39
6 changed files with 138 additions and 143 deletions
|
|
@ -58,7 +58,7 @@ interface IMeshService {
|
|||
*/
|
||||
void setOwner(in MeshUser user);
|
||||
|
||||
void setRemoteOwner(in int destNum, in byte []payload);
|
||||
void setRemoteOwner(in int requestId, in byte []payload);
|
||||
void getRemoteOwner(in int requestId, in int destNum);
|
||||
|
||||
/// Return my unique user ID string
|
||||
|
|
@ -91,11 +91,11 @@ interface IMeshService {
|
|||
void setConfig(in byte []payload);
|
||||
|
||||
/// Set and get a Config protobuf via admin packet
|
||||
void setRemoteConfig(in int destNum, in byte []payload);
|
||||
void setRemoteConfig(in int requestId, in int destNum, in byte []payload);
|
||||
void getRemoteConfig(in int requestId, in int destNum, in int configTypeValue);
|
||||
|
||||
/// Set and get a ModuleConfig protobuf via admin packet
|
||||
void setModuleConfig(in int destNum, in byte []payload);
|
||||
void setModuleConfig(in int requestId, in int destNum, in byte []payload);
|
||||
void getModuleConfig(in int requestId, in int destNum, in int moduleConfigTypeValue);
|
||||
|
||||
/// Set and get the Ext Notification Ringtone string via admin packet
|
||||
|
|
@ -111,7 +111,7 @@ interface IMeshService {
|
|||
void setChannel(in byte []payload);
|
||||
|
||||
/// Set and get a Channel protobuf via admin packet
|
||||
void setRemoteChannel(in int destNum, in byte []payload);
|
||||
void setRemoteChannel(in int requestId, in int destNum, in byte []payload);
|
||||
void getRemoteChannel(in int requestId, in int destNum, in int channelIndex);
|
||||
|
||||
/// Send beginEditSettings admin packet to nodeNum
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ class UIViewModel @Inject constructor(
|
|||
private val _ourNodeInfo = MutableStateFlow<NodeInfo?>(null)
|
||||
val ourNodeInfo: StateFlow<NodeInfo?> = _ourNodeInfo
|
||||
|
||||
private val requestId = MutableStateFlow<Int?>(null)
|
||||
private val requestIds = MutableStateFlow<HashMap<Int, Boolean>>(hashMapOf())
|
||||
private val _radioConfigState = MutableStateFlow(RadioConfigState())
|
||||
val radioConfigState: StateFlow<RadioConfigState> = _radioConfigState
|
||||
|
||||
|
|
@ -169,11 +169,11 @@ class UIViewModel @Inject constructor(
|
|||
}.launchIn(viewModelScope)
|
||||
|
||||
viewModelScope.launch {
|
||||
combine(meshLogRepository.getAllLogs(9), requestId) { list, id ->
|
||||
list.takeIf { id != null }?.firstOrNull { it.meshPacket?.decoded?.requestId == id }
|
||||
}.collect(::processPacketResponse)
|
||||
combine(meshLogRepository.getAllLogs(9), requestIds) { list, ids ->
|
||||
val unprocessed = ids.filterValues { !it }.keys.ifEmpty { return@combine emptyList() }
|
||||
list.filter { log -> log.meshPacket?.decoded?.requestId in unprocessed }
|
||||
}.collect { it.forEach(::processPacketResponse) }
|
||||
}
|
||||
|
||||
debug("ViewModel created")
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +255,15 @@ class UIViewModel @Inject constructor(
|
|||
val packetId = service.packetId
|
||||
try {
|
||||
requestAction(service, packetId, destNum)
|
||||
requestId.value = packetId
|
||||
requestIds.update { it.apply { put(packetId, false) } }
|
||||
_radioConfigState.update { state ->
|
||||
if (state.responseState is ResponseState.Loading) {
|
||||
val total = maxOf(requestIds.value.size, state.responseState.total)
|
||||
state.copy(responseState = state.responseState.copy(total = total))
|
||||
} else {
|
||||
state.copy(responseState = ResponseState.Loading())
|
||||
}
|
||||
}
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("$errorMessage: ${ex.message}")
|
||||
}
|
||||
|
|
@ -268,18 +276,44 @@ class UIViewModel @Inject constructor(
|
|||
"Request getOwner error"
|
||||
)
|
||||
|
||||
private fun setRemoteChannel(destNum: Int, channel: ChannelProtos.Channel) = request(
|
||||
destNum,
|
||||
{ service, packetId, dest ->
|
||||
service.setRemoteChannel(packetId, dest, channel.toByteArray())
|
||||
},
|
||||
"Request setRemoteChannel error"
|
||||
)
|
||||
|
||||
fun getChannel(destNum: Int, index: Int) = request(
|
||||
destNum,
|
||||
{ service, packetId, dest -> service.getRemoteChannel(packetId, dest, index) },
|
||||
"Request getChannel error"
|
||||
)
|
||||
|
||||
fun setRemoteConfig(destNum: Int, config: Config) = request(
|
||||
destNum,
|
||||
{ service, packetId, dest ->
|
||||
_radioConfigState.update { it.copy(radioConfig = config) }
|
||||
service.setRemoteConfig(packetId, dest, config.toByteArray())
|
||||
},
|
||||
"Request setConfig error",
|
||||
)
|
||||
|
||||
fun getConfig(destNum: Int, configType: Int) = request(
|
||||
destNum,
|
||||
{ service, packetId, dest -> service.getRemoteConfig(packetId, dest, configType) },
|
||||
"Request getConfig error",
|
||||
)
|
||||
|
||||
fun setModuleConfig(destNum: Int, config: ModuleConfig) = request(
|
||||
destNum,
|
||||
{ service, packetId, dest ->
|
||||
_radioConfigState.update { it.copy(moduleConfig = config) }
|
||||
service.setModuleConfig(packetId, dest, config.toByteArray())
|
||||
},
|
||||
"Request setConfig error",
|
||||
)
|
||||
|
||||
fun getModuleConfig(destNum: Int, configType: Int) = request(
|
||||
destNum,
|
||||
{ service, packetId, dest -> service.getModuleConfig(packetId, dest, configType) },
|
||||
|
|
@ -472,16 +506,6 @@ class UIViewModel @Inject constructor(
|
|||
meshService?.setConfig(config.toByteArray())
|
||||
}
|
||||
|
||||
fun setRemoteConfig(destNum: Int, config: Config) {
|
||||
_radioConfigState.update { it.copy(radioConfig = config) }
|
||||
meshService?.setRemoteConfig(destNum, config.toByteArray())
|
||||
}
|
||||
|
||||
fun setModuleConfig(destNum: Int, config: ModuleConfig) {
|
||||
_radioConfigState.update { it.copy(moduleConfig = config) }
|
||||
meshService?.setModuleConfig(destNum, config.toByteArray())
|
||||
}
|
||||
|
||||
fun setModuleConfig(config: ModuleConfig) {
|
||||
setModuleConfig(myNodeNum ?: return, config)
|
||||
}
|
||||
|
|
@ -515,6 +539,7 @@ class UIViewModel @Inject constructor(
|
|||
if (destNum == myNodeNum) viewModelScope.launch {
|
||||
radioConfigRepository.replaceAllSettings(new)
|
||||
}
|
||||
_radioConfigState.update { it.copy(channelList = new) }
|
||||
}
|
||||
|
||||
private fun updateChannels(
|
||||
|
|
@ -542,14 +567,6 @@ class UIViewModel @Inject constructor(
|
|||
this._channelSet = channelSet.protobuf
|
||||
}
|
||||
|
||||
private fun setRemoteChannel(destNum: Int, channel: ChannelProtos.Channel) {
|
||||
try {
|
||||
meshService?.setRemoteChannel(destNum, channel.toByteArray())
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("Can't set channel on radio ${ex.message}")
|
||||
}
|
||||
}
|
||||
|
||||
val provideLocation = object : MutableLiveData<Boolean>(preferences.getBoolean("provide-location", false)) {
|
||||
override fun setValue(value: Boolean) {
|
||||
super.setValue(value)
|
||||
|
|
@ -564,15 +581,14 @@ class UIViewModel @Inject constructor(
|
|||
setRemoteOwner(myNodeNum ?: return, user)
|
||||
}
|
||||
|
||||
fun setRemoteOwner(destNum: Int, user: User) {
|
||||
try {
|
||||
// Note: we use ?. here because we might be running in the emulator
|
||||
meshService?.setRemoteOwner(destNum, user.toByteArray())
|
||||
fun setRemoteOwner(destNum: Int, user: User) = request(
|
||||
destNum,
|
||||
{ service, packetId, _ ->
|
||||
_radioConfigState.update { it.copy(userConfig = user) }
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("Can't set username on device, is device offline? ${ex.message}")
|
||||
}
|
||||
}
|
||||
service.setRemoteOwner(packetId, user.toByteArray())
|
||||
},
|
||||
"Request setOwner error",
|
||||
)
|
||||
|
||||
val adminChannelIndex: Int /** matches [MeshService.adminChannelIndex] **/
|
||||
get() = channelSet.settingsList.indexOfFirst { it.name.equals("admin", ignoreCase = true) }
|
||||
|
|
@ -723,6 +739,7 @@ class UIViewModel @Inject constructor(
|
|||
message.writeTo(outputStream)
|
||||
}
|
||||
}
|
||||
setResponseStateSuccess()
|
||||
} catch (ex: Exception) {
|
||||
val error = "${ex.javaClass.simpleName}: ${ex.message}"
|
||||
errormsg("Can't write file error: ${ex.message}")
|
||||
|
|
@ -763,6 +780,7 @@ class UIViewModel @Inject constructor(
|
|||
setModuleConfig(moduleConfig { audio = it.audio })
|
||||
setModuleConfig(moduleConfig { remoteHardware = it.remoteHardware })
|
||||
}
|
||||
setResponseStateSuccess()
|
||||
// meshService?.commitEditSettings()
|
||||
}
|
||||
|
||||
|
|
@ -806,17 +824,20 @@ class UIViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun clearPacketResponse() {
|
||||
requestIds.value = hashMapOf()
|
||||
_radioConfigState.update { it.copy(responseState = ResponseState.Empty) }
|
||||
}
|
||||
|
||||
fun setResponseStateLoading(route: String) {
|
||||
_radioConfigState.value = RadioConfigState(
|
||||
route = route,
|
||||
responseState = ResponseState.Loading(total = 1),
|
||||
responseState = ResponseState.Loading(),
|
||||
)
|
||||
// channel editor is synchronous, so we don't use requestIds as total
|
||||
if (route == ConfigRoute.CHANNELS.name) setResponseStateTotal(maxChannels + 1)
|
||||
}
|
||||
|
||||
fun setResponseStateTotal(total: Int) {
|
||||
private fun setResponseStateTotal(total: Int) {
|
||||
_radioConfigState.update { state ->
|
||||
if (state.responseState is ResponseState.Loading) {
|
||||
state.copy(responseState = state.responseState.copy(total = total))
|
||||
|
|
@ -826,6 +847,16 @@ class UIViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun setResponseStateSuccess() {
|
||||
_radioConfigState.update { state ->
|
||||
if (state.responseState is ResponseState.Loading) {
|
||||
state.copy(responseState = ResponseState.Success(true))
|
||||
} else {
|
||||
state // Return the unchanged state for other response states
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setResponseStateError(error: String) {
|
||||
_radioConfigState.update { it.copy(responseState = ResponseState.Error(error)) }
|
||||
}
|
||||
|
|
@ -841,14 +872,6 @@ class UIViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun clearRemoteChannelList() {
|
||||
_radioConfigState.update { it.copy(channelList = emptyList()) }
|
||||
}
|
||||
|
||||
fun setRemoteChannelList(list: List<ChannelSettings>) {
|
||||
_radioConfigState.update { it.copy(channelList = list) }
|
||||
}
|
||||
|
||||
private val _tracerouteResponse = MutableLiveData<String?>(null)
|
||||
val tracerouteResponse: LiveData<String?> get() = _tracerouteResponse
|
||||
|
||||
|
|
@ -859,8 +882,7 @@ class UIViewModel @Inject constructor(
|
|||
private fun processPacketResponse(log: MeshLog?) {
|
||||
val packet = log?.meshPacket ?: return
|
||||
val data = packet.decoded
|
||||
val fromStr = packet.from.toUInt()
|
||||
requestId.value = null
|
||||
requestIds.update { it.apply { put(data.requestId, true) } }
|
||||
|
||||
if (data?.portnumValue == Portnums.PortNum.TRACEROUTE_APP_VALUE) {
|
||||
val parsed = MeshProtos.RouteDiscovery.parseFrom(data.payload)
|
||||
|
|
@ -874,25 +896,26 @@ class UIViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
val destNum = destNode.value?.num ?: return
|
||||
val destStr = destNum.toUInt()
|
||||
val debugMsg = "requestId: ${data.requestId.toUInt()} to: ${destNum.toUInt()} received %s from: ${packet.from.toUInt()}"
|
||||
|
||||
if (data?.portnumValue == Portnums.PortNum.ROUTING_APP_VALUE) {
|
||||
val parsed = MeshProtos.Routing.parseFrom(data.payload)
|
||||
debug("packet for destNum $destStr received ${parsed.errorReason} from $fromStr")
|
||||
debug(debugMsg.format(parsed.errorReason.name))
|
||||
if (parsed.errorReason != MeshProtos.Routing.Error.NONE) {
|
||||
setResponseStateError(parsed.errorReason.name)
|
||||
} else if (packet.from == destNum) {
|
||||
_radioConfigState.update { it.copy(responseState = ResponseState.Success(true)) }
|
||||
if (requestIds.value.filterValues { !it }.isEmpty()) setResponseStateSuccess()
|
||||
else incrementCompleted()
|
||||
}
|
||||
}
|
||||
if (data?.portnumValue == Portnums.PortNum.ADMIN_APP_VALUE) {
|
||||
val parsed = AdminProtos.AdminMessage.parseFrom(data.payload)
|
||||
debug("packet for destNum $destStr received ${parsed.payloadVariantCase} from $fromStr")
|
||||
debug(debugMsg.format(parsed.payloadVariantCase.name))
|
||||
if (destNum != packet.from) {
|
||||
setResponseStateError("Unexpected sender: $fromStr instead of $destStr.")
|
||||
setResponseStateError("Unexpected sender: ${packet.from.toUInt()} instead of ${destNum.toUInt()}.")
|
||||
return
|
||||
}
|
||||
// check destination: lora config or channel editor
|
||||
// check if destination is channel editor
|
||||
val goChannels = radioConfigState.value.route == ConfigRoute.CHANNELS.name
|
||||
when (parsed.payloadVariantCase) {
|
||||
AdminProtos.AdminMessage.PayloadVariantCase.GET_CHANNEL_RESPONSE -> {
|
||||
|
|
@ -900,23 +923,18 @@ class UIViewModel @Inject constructor(
|
|||
// Stop once we get to the first disabled entry
|
||||
if (response.role != ChannelProtos.Channel.Role.DISABLED) {
|
||||
_radioConfigState.update { state ->
|
||||
val updatedList = state.channelList.toMutableList().apply {
|
||||
state.copy(channelList = state.channelList.toMutableList().apply {
|
||||
add(response.index, response.settings)
|
||||
}
|
||||
state.copy(channelList = updatedList)
|
||||
})
|
||||
}
|
||||
incrementCompleted()
|
||||
if (response.index + 1 < maxChannels && goChannels) {
|
||||
// Not done yet, request next channel
|
||||
getChannel(destNum, response.index + 1)
|
||||
} else {
|
||||
// Received max channels, get lora config (for default channel names)
|
||||
getConfig(destNum, AdminProtos.AdminMessage.ConfigType.LORA_CONFIG_VALUE)
|
||||
}
|
||||
} else {
|
||||
// Received last channel, get lora config (for default channel names)
|
||||
// Received last channel, update total and start channel editor
|
||||
setResponseStateTotal(response.index + 1)
|
||||
getConfig(destNum, AdminProtos.AdminMessage.ConfigType.LORA_CONFIG_VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -948,13 +966,11 @@ class UIViewModel @Inject constructor(
|
|||
it.copy(cannedMessageMessages = parsed.getCannedMessageModuleMessagesResponse)
|
||||
}
|
||||
incrementCompleted()
|
||||
getModuleConfig(destNum, AdminProtos.AdminMessage.ModuleConfigType.CANNEDMSG_CONFIG_VALUE)
|
||||
}
|
||||
|
||||
AdminProtos.AdminMessage.PayloadVariantCase.GET_RINGTONE_RESPONSE -> {
|
||||
_radioConfigState.update { it.copy(ringtone = parsed.getRingtoneResponse) }
|
||||
incrementCompleted()
|
||||
getModuleConfig(destNum, AdminProtos.AdminMessage.ModuleConfigType.EXTNOTIF_CONFIG_VALUE)
|
||||
}
|
||||
|
||||
else -> TODO()
|
||||
|
|
|
|||
|
|
@ -1483,35 +1483,23 @@ class MeshService : Service(), Logging {
|
|||
/**
|
||||
* Send setOwner admin packet with [MeshProtos.User] protobuf
|
||||
*/
|
||||
fun setOwner(meshUser: MeshUser) = with(meshUser) {
|
||||
private fun setOwner(packetId: Int, user: MeshProtos.User) = with(user) {
|
||||
val dest = nodeDBbyID[id]
|
||||
if (dest != null) {
|
||||
val old = dest.user
|
||||
if (longName == old?.longName && shortName == old.shortName && isLicensed == old.isLicensed)
|
||||
debug("Ignoring nop owner change")
|
||||
else {
|
||||
debug("SetOwner Id: $id longName: ${longName.anonymize} shortName: $shortName isLicensed: $isLicensed")
|
||||
?: throw Exception("Can't set user without a NodeInfo") // this shouldn't happen
|
||||
val old = dest.user!!
|
||||
if (longName == old.longName && shortName == old.shortName && isLicensed == old.isLicensed) {
|
||||
debug("Ignoring nop owner change")
|
||||
} else {
|
||||
debug("setOwner Id: $id longName: ${longName.anonymize} shortName: $shortName isLicensed: $isLicensed")
|
||||
|
||||
val user = MeshProtos.User.newBuilder().also {
|
||||
it.longName = longName
|
||||
it.shortName = shortName
|
||||
it.hwModel = hwModel
|
||||
it.isLicensed = isLicensed
|
||||
}.build()
|
||||
// Also update our own map for our nodeNum, by handling the packet just like packets from other users
|
||||
handleReceivedUser(dest.num, user)
|
||||
|
||||
// Also update our own map for our nodenum, by handling the packet just like packets from other users
|
||||
handleReceivedUser(dest.num, user)
|
||||
|
||||
// encapsulate our payload in the proper protobufs and fire it off
|
||||
val packet = newMeshPacketTo(dest.num).buildAdminPacket {
|
||||
setOwner = user
|
||||
}
|
||||
|
||||
// send the packet into the mesh
|
||||
sendToRadio(packet)
|
||||
}
|
||||
} else
|
||||
throw Exception("Can't set user without a node info") // this shouldn't happen
|
||||
// encapsulate our payload in the proper protobuf and fire it off
|
||||
sendToRadio(newMeshPacketTo(dest.num).buildAdminPacket(id = packetId) {
|
||||
setOwner = user
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1614,14 +1602,12 @@ class MeshService : Service(), Logging {
|
|||
override fun getPacketId() = toRemoteExceptions { generatePacketId() }
|
||||
|
||||
override fun setOwner(user: MeshUser) = toRemoteExceptions {
|
||||
this@MeshService.setOwner(user)
|
||||
setOwner(generatePacketId(), user.toProto())
|
||||
}
|
||||
|
||||
override fun setRemoteOwner(destNum: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
override fun setRemoteOwner(id: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
val parsed = MeshProtos.User.parseFrom(payload)
|
||||
sendToRadio(newMeshPacketTo(destNum).buildAdminPacket {
|
||||
setOwner = parsed
|
||||
})
|
||||
setOwner(id, parsed)
|
||||
}
|
||||
|
||||
override fun getRemoteOwner(id: Int, destNum: Int) = toRemoteExceptions {
|
||||
|
|
@ -1674,14 +1660,14 @@ class MeshService : Service(), Logging {
|
|||
/** Send our current radio config to the device
|
||||
*/
|
||||
override fun setConfig(payload: ByteArray) = toRemoteExceptions {
|
||||
setRemoteConfig(myNodeNum, payload)
|
||||
setRemoteConfig(generatePacketId(), myNodeNum, payload)
|
||||
}
|
||||
|
||||
override fun setRemoteConfig(destNum: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
override fun setRemoteConfig(id: Int, num: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
debug("Setting new radio config!")
|
||||
val config = ConfigProtos.Config.parseFrom(payload)
|
||||
sendToRadio(newMeshPacketTo(destNum).buildAdminPacket { setConfig = config })
|
||||
if (destNum == myNodeNum) setLocalConfig(config) // Update our local copy
|
||||
sendToRadio(newMeshPacketTo(num).buildAdminPacket(id = id) { setConfig = config })
|
||||
if (num == myNodeNum) setLocalConfig(config) // Update our local copy
|
||||
}
|
||||
|
||||
override fun getRemoteConfig(id: Int, destNum: Int, config: Int) = toRemoteExceptions {
|
||||
|
|
@ -1692,11 +1678,11 @@ class MeshService : Service(), Logging {
|
|||
|
||||
/** Send our current module config to the device
|
||||
*/
|
||||
override fun setModuleConfig(destNum: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
override fun setModuleConfig(id: Int, num: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
debug("Setting new module config!")
|
||||
val config = ModuleConfigProtos.ModuleConfig.parseFrom(payload)
|
||||
sendToRadio(newMeshPacketTo(destNum).buildAdminPacket { setModuleConfig = config })
|
||||
if (destNum == myNodeNum) setLocalModuleConfig(config) // Update our local copy
|
||||
sendToRadio(newMeshPacketTo(num).buildAdminPacket(id = id) { setModuleConfig = config })
|
||||
if (num == myNodeNum) setLocalModuleConfig(config) // Update our local copy
|
||||
}
|
||||
|
||||
override fun getModuleConfig(id: Int, destNum: Int, config: Int) = toRemoteExceptions {
|
||||
|
|
@ -1730,12 +1716,12 @@ class MeshService : Service(), Logging {
|
|||
}
|
||||
|
||||
override fun setChannel(payload: ByteArray?) = toRemoteExceptions {
|
||||
setRemoteChannel(myNodeNum, payload)
|
||||
setRemoteChannel(generatePacketId(), myNodeNum, payload)
|
||||
}
|
||||
|
||||
override fun setRemoteChannel(destNum: Int, payload: ByteArray?) = toRemoteExceptions {
|
||||
override fun setRemoteChannel(id: Int, num: Int, payload: ByteArray?) = toRemoteExceptions {
|
||||
val channel = ChannelProtos.Channel.parseFrom(payload)
|
||||
sendToRadio(newMeshPacketTo(destNum).buildAdminPacket { setChannel = channel })
|
||||
sendToRadio(newMeshPacketTo(num).buildAdminPacket(id = id) { setChannel = channel })
|
||||
}
|
||||
|
||||
override fun getRemoteChannel(id: Int, destNum: Int, index: Int) = toRemoteExceptions {
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ private fun getName(route: Any): String = when (route) {
|
|||
*/
|
||||
sealed class ResponseState<out T> {
|
||||
data object Empty : ResponseState<Nothing>()
|
||||
data class Loading(var total: Int = 0, var completed: Int = 0) : ResponseState<Nothing>()
|
||||
data class Loading(var total: Int = 1, var completed: Int = 0) : ResponseState<Nothing>()
|
||||
data class Success<T>(val result: T) : ResponseState<T>()
|
||||
data class Error(val error: String) : ResponseState<Nothing>()
|
||||
}
|
||||
|
|
@ -231,7 +231,6 @@ fun RadioConfigNavHost(
|
|||
|
||||
val destNum = node?.num ?: 0
|
||||
val isLocal = destNum == viewModel.myNodeNum
|
||||
val maxChannels = viewModel.maxChannels
|
||||
|
||||
val radioConfigState by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
var location by remember(node) { mutableStateOf(node?.position) } // FIXME
|
||||
|
|
@ -314,12 +313,15 @@ fun RadioConfigNavHost(
|
|||
onRouteClick = { route ->
|
||||
viewModel.setResponseStateLoading(getName(route))
|
||||
when (route) {
|
||||
ConfigRoute.USER -> { viewModel.getOwner(destNum) }
|
||||
ConfigRoute.CHANNELS -> {
|
||||
viewModel.setResponseStateTotal(maxChannels + 1) // for lora config
|
||||
viewModel.clearRemoteChannelList()
|
||||
viewModel.getChannel(destNum, 0)
|
||||
ConfigRoute.USER -> {
|
||||
viewModel.getOwner(destNum)
|
||||
}
|
||||
|
||||
ConfigRoute.CHANNELS -> {
|
||||
viewModel.getChannel(destNum, 0)
|
||||
viewModel.getConfig(destNum, ConfigRoute.LORA.configType)
|
||||
}
|
||||
|
||||
"IMPORT" -> {
|
||||
viewModel.clearPacketResponse()
|
||||
viewModel.setDeviceProfile(null)
|
||||
|
|
@ -329,6 +331,7 @@ fun RadioConfigNavHost(
|
|||
}
|
||||
importConfigLauncher.launch(intent)
|
||||
}
|
||||
|
||||
"EXPORT" -> {
|
||||
viewModel.clearPacketResponse()
|
||||
showEditDeviceProfileDialog = true
|
||||
|
|
@ -350,23 +353,20 @@ fun RadioConfigNavHost(
|
|||
viewModel.requestNodedbReset(destNum)
|
||||
}
|
||||
|
||||
ConfigRoute.LORA -> {
|
||||
viewModel.setResponseStateTotal(2)
|
||||
viewModel.clearRemoteChannelList()
|
||||
viewModel.getChannel(destNum, 0)
|
||||
}
|
||||
is ConfigRoute -> {
|
||||
if (route == ConfigRoute.LORA) {
|
||||
viewModel.getChannel(destNum, 0)
|
||||
}
|
||||
viewModel.getConfig(destNum, route.configType)
|
||||
}
|
||||
ModuleRoute.CANNED_MESSAGE -> {
|
||||
viewModel.setResponseStateTotal(2)
|
||||
viewModel.getCannedMessages(destNum)
|
||||
}
|
||||
ModuleRoute.EXTERNAL_NOTIFICATION -> {
|
||||
viewModel.setResponseStateTotal(2)
|
||||
viewModel.getRingtone(destNum)
|
||||
}
|
||||
|
||||
is ModuleRoute -> {
|
||||
if (route == ModuleRoute.CANNED_MESSAGE) {
|
||||
viewModel.getCannedMessages(destNum)
|
||||
}
|
||||
if (route == ModuleRoute.EXTERNAL_NOTIFICATION) {
|
||||
viewModel.getRingtone(destNum)
|
||||
}
|
||||
viewModel.getModuleConfig(destNum, route.configType)
|
||||
}
|
||||
}
|
||||
|
|
@ -387,10 +387,9 @@ fun RadioConfigNavHost(
|
|||
settingsList = radioConfigState.channelList,
|
||||
modemPresetName = Channel(loraConfig = radioConfigState.radioConfig.lora).name,
|
||||
enabled = connected,
|
||||
maxChannels = maxChannels,
|
||||
maxChannels = viewModel.maxChannels,
|
||||
onPositiveClicked = { channelListInput ->
|
||||
viewModel.updateChannels(destNum, radioConfigState.channelList, channelListInput)
|
||||
viewModel.setRemoteChannelList(channelListInput)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ import androidx.compose.material.icons.twotone.Add
|
|||
import androidx.compose.material.icons.twotone.Close
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -94,9 +94,7 @@ fun ChannelSettingsItemList(
|
|||
onPositiveClicked: (List<ChannelSettings>) -> Unit,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val settingsListInput = remember {
|
||||
mutableStateListOf<ChannelSettings>().apply { addAll(settingsList) }
|
||||
}
|
||||
val settingsListInput = remember(settingsList) { settingsList.toMutableStateList() }
|
||||
|
||||
val isEditing: Boolean = settingsList.size != settingsListInput.size
|
||||
|| settingsList.zip(settingsListInput).any { (item1, item2) -> item1 != item2 }
|
||||
|
|
@ -172,10 +170,12 @@ fun ChannelSettingsItemList(
|
|||
) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
settingsListInput.add(channelSettings {
|
||||
psk = Channel.default.settings.psk
|
||||
})
|
||||
showEditChannelDialog = settingsListInput.lastIndex
|
||||
if (maxChannels > settingsListInput.size) {
|
||||
settingsListInput.add(channelSettings {
|
||||
psk = Channel.default.settings.psk
|
||||
})
|
||||
showEditChannelDialog = settingsListInput.lastIndex
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) { Icon(Icons.TwoTone.Add, stringResource(R.string.add)) }
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ fun <T> PacketResponseStateDialog(
|
|||
if (state.total == state.completed) onComplete()
|
||||
}
|
||||
if (state is ResponseState.Success) {
|
||||
Text("Success!")
|
||||
Text("Delivery confirmed.")
|
||||
}
|
||||
if (state is ResponseState.Error) {
|
||||
Text(text = "Error\n", textAlign = TextAlign.Center)
|
||||
|
|
@ -67,13 +67,7 @@ fun <T> PacketResponseStateDialog(
|
|||
Button(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
) {
|
||||
if (state is ResponseState.Loading) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
} else {
|
||||
Text(stringResource(R.string.close))
|
||||
}
|
||||
}
|
||||
) { Text(stringResource(R.string.close)) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue