diff --git a/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt b/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt index ae937d896..cf70a8ae6 100644 --- a/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt @@ -16,12 +16,14 @@ import com.geeksville.mesh.MyNodeInfo import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.Portnums import com.geeksville.mesh.Position +import com.geeksville.mesh.R import com.geeksville.mesh.android.Logging import com.geeksville.mesh.config import com.geeksville.mesh.database.entity.toNodeInfo import com.geeksville.mesh.deviceProfile import com.geeksville.mesh.moduleConfig import com.geeksville.mesh.repository.datastore.RadioConfigRepository +import com.geeksville.mesh.ui.AdminRoute import com.geeksville.mesh.ui.ConfigRoute import com.geeksville.mesh.ui.ResponseState import com.google.protobuf.MessageLite @@ -44,6 +46,7 @@ import javax.inject.Inject */ data class RadioConfigState( val route: String = "", + val metadata: MeshProtos.DeviceMetadata = MeshProtos.DeviceMetadata.getDefaultInstance(), val userConfig: MeshProtos.User = MeshProtos.User.getDefaultInstance(), val channelList: List = emptyList(), val radioConfig: ConfigProtos.Config = config {}, @@ -51,7 +54,9 @@ data class RadioConfigState( val ringtone: String = "", val cannedMessageMessages: String = "", val responseState: ResponseState = ResponseState.Empty, -) +) { + fun hasMetadata() = metadata != MeshProtos.DeviceMetadata.getDefaultInstance() +} @HiltViewModel class RadioConfigViewModel @Inject constructor( @@ -247,30 +252,55 @@ class RadioConfigViewModel @Inject constructor( "Request getCannedMessages error" ) - fun requestShutdown(destNum: Int) = request( + private fun requestShutdown(destNum: Int) = request( destNum, { service, packetId, dest -> service.requestShutdown(packetId, dest) }, "Request shutdown error" ) - fun requestReboot(destNum: Int) = request( + private fun requestReboot(destNum: Int) = request( destNum, { service, packetId, dest -> service.requestReboot(packetId, dest) }, "Request reboot error" ) - fun requestFactoryReset(destNum: Int) = request( + private fun requestFactoryReset(destNum: Int) = request( destNum, { service, packetId, dest -> service.requestFactoryReset(packetId, dest) }, "Request factory reset error" ) - fun requestNodedbReset(destNum: Int) = request( + private fun requestNodedbReset(destNum: Int) = request( destNum, { service, packetId, dest -> service.requestNodedbReset(packetId, dest) }, "Request NodeDB reset error" ) + private fun sendAdminRequest(destNum: Int) { + when (radioConfigState.value.route) { + AdminRoute.REBOOT.name -> requestReboot(destNum) + AdminRoute.SHUTDOWN.name -> with(radioConfigState.value) { + if (hasMetadata() && !metadata.canShutdown) { + setResponseStateError(app.getString(R.string.cant_shutdown)) + } else { + requestShutdown(destNum) + } + } + + AdminRoute.FACTORY_RESET.name -> requestFactoryReset(destNum) + AdminRoute.NODEDB_RESET.name -> requestNodedbReset(destNum) + } + } + + fun getSessionPasskey(destNum: Int) { + if (radioConfigState.value.hasMetadata()) { + sendAdminRequest(destNum) + } else { + getConfig(destNum, AdminProtos.AdminMessage.ConfigType.SESSIONKEY_CONFIG_VALUE) + setResponseStateTotal(2) + } + } + fun setFixedPosition(destNum: Int, position: Position) { try { meshService?.setFixedPosition(destNum, position) @@ -444,9 +474,12 @@ class RadioConfigViewModel @Inject constructor( setResponseStateError("Unexpected sender: ${packet.from.toUInt()} instead of ${destNum.toUInt()}.") return } - // check if destination is channel editor - val goChannels = route == ConfigRoute.CHANNELS.name when (parsed.payloadVariantCase) { + AdminProtos.AdminMessage.PayloadVariantCase.GET_DEVICE_METADATA_RESPONSE -> { + _radioConfigState.update { it.copy(metadata = parsed.getDeviceMetadataResponse) } + incrementCompleted() + } + AdminProtos.AdminMessage.PayloadVariantCase.GET_CHANNEL_RESPONSE -> { val response = parsed.getChannelResponse // Stop once we get to the first disabled entry @@ -457,7 +490,7 @@ class RadioConfigViewModel @Inject constructor( }) } incrementCompleted() - if (response.index + 1 < maxChannels && goChannels) { + if (response.index + 1 < maxChannels && route == ConfigRoute.CHANNELS.name) { // Not done yet, request next channel getChannel(destNum, response.index + 1) } @@ -504,6 +537,10 @@ class RadioConfigViewModel @Inject constructor( else -> TODO() } + + if (AdminRoute.entries.any { it.name == route }) { + sendAdminRequest(destNum) + } requestIds.update { it.apply { remove(data.requestId) } } } } diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 7dee87f00..f39481851 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -1794,7 +1794,11 @@ class MeshService : Service(), Logging { override fun getRemoteConfig(id: Int, destNum: Int, config: Int) = toRemoteExceptions { sendToRadio(newMeshPacketTo(destNum).buildAdminPacket(id = id, wantResponse = true) { - getConfigRequestValue = config + if (config == AdminProtos.AdminMessage.ConfigType.SESSIONKEY_CONFIG_VALUE) { + getDeviceMetadataRequest = true + } else { + getConfigRequestValue = config + } }) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt index 5db4215a3..04ecd5d01 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt @@ -163,6 +163,14 @@ class DeviceSettingsFragment : ScreenFragment("Radio Configuration"), Logging { } } +enum class AdminRoute(@StringRes val title: Int) { + REBOOT(R.string.reboot), + SHUTDOWN(R.string.shutdown), + FACTORY_RESET(R.string.factory_reset), + NODEDB_RESET(R.string.nodedb_reset), + ; +} + // Config (configType = AdminProtos.AdminMessage.ConfigType) enum class ConfigRoute(val title: String, val configType: Int = 0) { USER("User"), @@ -197,6 +205,7 @@ enum class ModuleRoute(val title: String, val configType: Int = 0) { } private fun getName(route: Any): String = when (route) { + is AdminRoute -> route.name is ConfigRoute -> route.name is ModuleRoute -> route.name else -> "" @@ -304,8 +313,11 @@ fun RadioConfigNavHost( }, onComplete = { val route = radioConfigState.route - if (route.isNotEmpty()) navController.navigate(route) - viewModel.clearPacketResponse() + if (ConfigRoute.entries.any { it.name == route } || + ModuleRoute.entries.any { it.name == route }) { + navController.navigate(route) + viewModel.clearPacketResponse() + } } ) @@ -346,20 +358,8 @@ fun RadioConfigNavHost( showEditDeviceProfileDialog = true } - "REBOOT" -> { - viewModel.requestReboot(destNum) - } - - "SHUTDOWN" -> { - viewModel.requestShutdown(destNum) - } - - "FACTORY_RESET" -> { - viewModel.requestFactoryReset(destNum) - } - - "NODEDB_RESET" -> { - viewModel.requestNodedbReset(destNum) + is AdminRoute -> { + viewModel.getSessionPasskey(destNum) } is ConfigRoute -> { @@ -753,10 +753,7 @@ private fun RadioSettingsScreen( item { NavCard("Export configuration", enabled = enabled) { onRouteClick("EXPORT") } } } - item { NavButton(R.string.reboot, enabled) { onRouteClick("REBOOT") } } - item { NavButton(R.string.shutdown, enabled) { onRouteClick("SHUTDOWN") } } - item { NavButton(R.string.factory_reset, enabled) { onRouteClick("FACTORY_RESET") } } - item { NavButton(R.string.nodedb_reset, enabled) { onRouteClick("NODEDB_RESET") } } + items(AdminRoute.entries) { NavButton(it.title, enabled) { onRouteClick(it) } } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 61d4b346e..bf561f7a8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -148,6 +148,7 @@ System default Resend Shutdown + Shutdown not supported on this device Reboot Traceroute Show Introduction