refactor: move `RadioConfig` files to separate package

pull/1541/head
andrekir 2025-01-09 20:01:21 -03:00 zatwierdzone przez Andre K
rodzic 7794c08190
commit ad9a3a5e49
39 zmienionych plików z 501 dodań i 357 usunięć

Wyświetl plik

@ -27,7 +27,7 @@ import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
import com.geeksville.mesh.R
import com.geeksville.mesh.deviceProfile
import com.geeksville.mesh.position
import com.geeksville.mesh.ui.components.config.EditDeviceProfileDialog
import com.geeksville.mesh.ui.radioconfig.components.EditDeviceProfileDialog
import org.junit.Assert
import org.junit.Rule
import org.junit.Test

Wyświetl plik

@ -0,0 +1,197 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.navigation
import androidx.compose.runtime.remember
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import com.geeksville.mesh.ui.Route
import com.geeksville.mesh.ui.radioconfig.RadioConfigScreen
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
import com.geeksville.mesh.ui.radioconfig.components.AmbientLightingConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.AudioConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.BluetoothConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.CannedMessageConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.ChannelConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.DetectionSensorConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.DeviceConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.DisplayConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.ExternalNotificationConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.LoRaConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.MQTTConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.NeighborInfoConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.NetworkConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.PaxcounterConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.PositionConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.PowerConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.RangeTestConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.RemoteHardwareConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.SecurityConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.SerialConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.StoreForwardConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.TelemetryConfigScreen
import com.geeksville.mesh.ui.radioconfig.components.UserConfigScreen
@Suppress("LongMethod")
fun NavGraphBuilder.addRadioConfigSection(navController: NavController) {
composable<Route.RadioConfig> {
RadioConfigScreen(
onNavigate = navController::navigate,
)
}
composable<Route.User> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
UserConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.ChannelConfig> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
ChannelConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Device> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
DeviceConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Position> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
PositionConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Power> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
PowerConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Network> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
NetworkConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Display> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
DisplayConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.LoRa> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
LoRaConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Bluetooth> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
BluetoothConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Security> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
SecurityConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.MQTT> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
MQTTConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Serial> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
SerialConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.ExtNotification> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
ExternalNotificationConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.StoreForward> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
StoreForwardConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.RangeTest> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
RangeTestConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Telemetry> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
TelemetryConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.CannedMessage> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
CannedMessageConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Audio> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
AudioConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.RemoteHardware> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
RemoteHardwareConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.NeighborInfo> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
NeighborInfoConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.AmbientLighting> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
AmbientLightingConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.DetectionSensor> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
DetectionSensorConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
composable<Route.Paxcounter> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
PaxcounterConfigScreen(
viewModel = hiltViewModel<RadioConfigViewModel>(parentEntry),
)
}
}

Wyświetl plik

@ -104,12 +104,12 @@ import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.ui.components.AdaptiveTwoPane
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.config.ChannelCard
import com.geeksville.mesh.ui.components.config.ChannelSelection
import com.geeksville.mesh.ui.components.config.EditChannelDialog
import com.geeksville.mesh.ui.components.dragContainer
import com.geeksville.mesh.ui.components.dragDropItemsIndexed
import com.geeksville.mesh.ui.components.rememberDragDropState
import com.geeksville.mesh.ui.radioconfig.components.ChannelCard
import com.geeksville.mesh.ui.radioconfig.components.ChannelSelection
import com.geeksville.mesh.ui.radioconfig.components.EditChannelDialog
import com.geeksville.mesh.ui.theme.AppTheme
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.journeyapps.barcodescanner.ScanContract

Wyświetl plik

@ -21,36 +21,10 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Forward
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.automirrored.filled.Message
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.filled.Bluetooth
import androidx.compose.material.icons.filled.CellTower
import androidx.compose.material.icons.filled.Cloud
import androidx.compose.material.icons.filled.DataUsage
import androidx.compose.material.icons.filled.DisplaySettings
import androidx.compose.material.icons.filled.LightMode
import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.People
import androidx.compose.material.icons.filled.PermScanWifi
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Power
import androidx.compose.material.icons.filled.Router
import androidx.compose.material.icons.filled.Security
import androidx.compose.material.icons.filled.Sensors
import androidx.compose.material.icons.filled.SettingsRemote
import androidx.compose.material.icons.filled.Speed
import androidx.compose.material.icons.filled.Usb
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
@ -64,11 +38,10 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.geeksville.mesh.MeshProtos.DeviceMetadata
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.navigation.addRadioConfigSection
import com.geeksville.mesh.ui.components.BaseScaffold
import com.geeksville.mesh.ui.components.DeviceMetricsScreen
import com.geeksville.mesh.ui.components.EnvironmentMetricsScreen
@ -76,31 +49,8 @@ import com.geeksville.mesh.ui.components.NodeMapScreen
import com.geeksville.mesh.ui.components.PositionLogScreen
import com.geeksville.mesh.ui.components.SignalMetricsScreen
import com.geeksville.mesh.ui.components.TracerouteLogScreen
import com.geeksville.mesh.ui.components.config.AmbientLightingConfigScreen
import com.geeksville.mesh.ui.components.config.AudioConfigScreen
import com.geeksville.mesh.ui.components.config.BluetoothConfigScreen
import com.geeksville.mesh.ui.components.config.CannedMessageConfigScreen
import com.geeksville.mesh.ui.components.config.ChannelConfigScreen
import com.geeksville.mesh.ui.components.config.DetectionSensorConfigScreen
import com.geeksville.mesh.ui.components.config.DeviceConfigScreen
import com.geeksville.mesh.ui.components.config.DisplayConfigScreen
import com.geeksville.mesh.ui.components.config.ExternalNotificationConfigScreen
import com.geeksville.mesh.ui.components.config.LoRaConfigScreen
import com.geeksville.mesh.ui.components.config.MQTTConfigScreen
import com.geeksville.mesh.ui.components.config.NeighborInfoConfigScreen
import com.geeksville.mesh.ui.components.config.NetworkConfigScreen
import com.geeksville.mesh.ui.components.config.PaxcounterConfigScreen
import com.geeksville.mesh.ui.components.config.PositionConfigScreen
import com.geeksville.mesh.ui.components.config.PowerConfigScreen
import com.geeksville.mesh.ui.components.config.RangeTestConfigScreen
import com.geeksville.mesh.ui.components.config.RemoteHardwareConfigScreen
import com.geeksville.mesh.ui.components.config.SecurityConfigScreen
import com.geeksville.mesh.ui.components.config.SerialConfigScreen
import com.geeksville.mesh.ui.components.config.StoreForwardConfigScreen
import com.geeksville.mesh.ui.components.config.TelemetryConfigScreen
import com.geeksville.mesh.ui.components.config.UserConfigScreen
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
import com.geeksville.mesh.ui.theme.AppTheme
import com.geeksville.mesh.util.UiText
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.serialization.Serializable
@ -164,13 +114,6 @@ class NavGraphFragment : ScreenFragment("NavGraph"), 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),
}
sealed interface Route {
@Serializable
data class Messages(val contactKey: String, val message: String = "") : Route
@ -180,7 +123,7 @@ sealed interface Route {
@Serializable
data class RadioConfig(val destNum: Int? = null) : Route
@Serializable data object User : Route
@Serializable data object Channels : Route
@Serializable data object ChannelConfig : Route
@Serializable data object Device : Route
@Serializable data object Position : Route
@Serializable data object Power : Route
@ -214,74 +157,6 @@ sealed interface Route {
@Serializable data object TracerouteLog : Route
}
// Config (type = AdminProtos.AdminMessage.ConfigType)
enum class ConfigRoute(val title: String, val route: Route, val icon: ImageVector?, val type: Int = 0) {
USER("User", Route.User, Icons.Default.Person, 0),
CHANNELS("Channels", Route.Channels, Icons.AutoMirrored.Default.List, 0),
DEVICE("Device", Route.Device, Icons.Default.Router, 0),
POSITION("Position", Route.Position, Icons.Default.LocationOn, 1),
POWER("Power", Route.Power, Icons.Default.Power, 2),
NETWORK("Network", Route.Network, Icons.Default.Wifi, 3),
DISPLAY("Display", Route.Display, Icons.Default.DisplaySettings, 4),
LORA("LoRa", Route.LoRa, Icons.Default.CellTower, 5),
BLUETOOTH("Bluetooth", Route.Bluetooth, Icons.Default.Bluetooth, 6),
SECURITY("Security", Route.Security, Icons.Default.Security, type = 7),
;
companion object {
fun filterExcludedFrom(metadata: DeviceMetadata?): List<ConfigRoute> = entries.filter {
when {
metadata == null -> true
it == BLUETOOTH -> metadata.hasBluetooth
it == NETWORK -> metadata.hasWifi || metadata.hasEthernet
else -> true // Include all other routes by default
}
}
}
}
// ModuleConfig (type = AdminProtos.AdminMessage.ModuleConfigType)
enum class ModuleRoute(val title: String, val route: Route, val icon: ImageVector?, val type: Int = 0) {
MQTT("MQTT", Route.MQTT, Icons.Default.Cloud, 0),
SERIAL("Serial", Route.Serial, Icons.Default.Usb, 1),
EXT_NOTIFICATION("External Notification", Route.ExtNotification, Icons.Default.Notifications, 2),
STORE_FORWARD("Store & Forward", Route.StoreForward, Icons.AutoMirrored.Default.Forward, 3),
RANGE_TEST("Range Test", Route.RangeTest, Icons.Default.Speed, 4),
TELEMETRY("Telemetry", Route.Telemetry, Icons.Default.DataUsage, 5),
CANNED_MESSAGE("Canned Message", Route.CannedMessage, Icons.AutoMirrored.Default.Message, 6),
AUDIO("Audio", Route.Audio, Icons.AutoMirrored.Default.VolumeUp, 7),
REMOTE_HARDWARE("Remote Hardware", Route.RemoteHardware, Icons.Default.SettingsRemote, 8),
NEIGHBOR_INFO("Neighbor Info", Route.NeighborInfo, Icons.Default.People, 9),
AMBIENT_LIGHTING("Ambient Lighting", Route.AmbientLighting, Icons.Default.LightMode, 10),
DETECTION_SENSOR("Detection Sensor", Route.DetectionSensor, Icons.Default.Sensors, 11),
PAXCOUNTER("Paxcounter", Route.Paxcounter, Icons.Default.PermScanWifi, 12),
;
val bitfield: Int get() = 1 shl ordinal
companion object {
fun filterExcludedFrom(metadata: DeviceMetadata?): List<ModuleRoute> = entries.filter {
when (metadata) {
null -> true
else -> metadata.excludedModules and it.bitfield == 0
}
}
}
}
/**
* Generic sealed class defines each possible state of a response.
*/
sealed class ResponseState<out T> {
data object Empty : 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: UiText) : ResponseState<Nothing>()
fun isWaiting() = this !is Empty
}
@Suppress("LongMethod")
@Composable
fun NavGraph(
navController: NavHostController = rememberNavController(),
@ -320,101 +195,7 @@ fun NavGraph(
val parentEntry = remember { navController.getBackStackEntry<Route.NodeDetail>() }
TracerouteLogScreen(hiltViewModel<MetricsViewModel>(parentEntry))
}
composable<Route.RadioConfig> {
RadioConfigScreen { navController.navigate(route = it) }
}
composable<Route.User> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
UserConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Channels> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
ChannelConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Device> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
DeviceConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Position> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
PositionConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Power> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
PowerConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Network> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
NetworkConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Display> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
DisplayConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.LoRa> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
LoRaConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Bluetooth> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
BluetoothConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Security> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
SecurityConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.MQTT> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
MQTTConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Serial> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
SerialConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.ExtNotification> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
ExternalNotificationConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.StoreForward> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
StoreForwardConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.RangeTest> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
RangeTestConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Telemetry> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
TelemetryConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.CannedMessage> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
CannedMessageConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Audio> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
AudioConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.RemoteHardware> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
RemoteHardwareConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.NeighborInfo> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
NeighborInfoConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.AmbientLighting> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
AmbientLightingConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.DetectionSensor> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
DetectionSensorConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
composable<Route.Paxcounter> {
val parentEntry = remember { navController.getBackStackEntry<Route.RadioConfig>() }
PaxcounterConfigScreen(hiltViewModel<RadioConfigViewModel>(parentEntry))
}
addRadioConfigSection(navController)
composable<Route.Share> { backStackEntry ->
val message = backStackEntry.toRoute<Route.Share>().message
ShareScreen(

Wyświetl plik

@ -97,6 +97,7 @@ import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.preview.NodePreviewParameterProvider
import com.geeksville.mesh.ui.radioconfig.NavCard
import com.geeksville.mesh.ui.theme.AppTheme
import com.geeksville.mesh.util.DistanceUnit
import com.geeksville.mesh.util.formatAgo

Wyświetl plik

@ -54,7 +54,7 @@ import com.geeksville.mesh.R
import com.geeksville.mesh.channelSet
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.Channel
import com.geeksville.mesh.ui.components.config.ChannelSelection
import com.geeksville.mesh.ui.radioconfig.components.ChannelSelection
/**
* Enables the user to select which channels to accept after scanning a QR code.

Wyświetl plik

@ -0,0 +1,28 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.radioconfig
import androidx.annotation.StringRes
import com.geeksville.mesh.R
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),
}

Wyświetl plik

@ -0,0 +1,60 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.radioconfig
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.filled.Bluetooth
import androidx.compose.material.icons.filled.CellTower
import androidx.compose.material.icons.filled.DisplaySettings
import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Power
import androidx.compose.material.icons.filled.Router
import androidx.compose.material.icons.filled.Security
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.ui.graphics.vector.ImageVector
import com.geeksville.mesh.MeshProtos.DeviceMetadata
import com.geeksville.mesh.ui.Route
@Suppress("MagicNumber")
// Config (type = AdminProtos.AdminMessage.ConfigType)
enum class ConfigRoute(val title: String, val route: Route, val icon: ImageVector?, val type: Int = 0) {
USER("User", Route.User, Icons.Default.Person, 0),
CHANNELS("Channels", Route.ChannelConfig, Icons.AutoMirrored.Default.List, 0),
DEVICE("Device", Route.Device, Icons.Default.Router, 0),
POSITION("Position", Route.Position, Icons.Default.LocationOn, 1),
POWER("Power", Route.Power, Icons.Default.Power, 2),
NETWORK("Network", Route.Network, Icons.Default.Wifi, 3),
DISPLAY("Display", Route.Display, Icons.Default.DisplaySettings, 4),
LORA("LoRa", Route.LoRa, Icons.Default.CellTower, 5),
BLUETOOTH("Bluetooth", Route.Bluetooth, Icons.Default.Bluetooth, 6),
SECURITY("Security", Route.Security, Icons.Default.Security, 7),
;
companion object {
fun filterExcludedFrom(metadata: DeviceMetadata?): List<ConfigRoute> = entries.filter {
when {
metadata == null -> true
it == BLUETOOTH -> metadata.hasBluetooth
it == NETWORK -> metadata.hasWifi || metadata.hasEthernet
else -> true // Include all other routes by default
}
}
}
}

Wyświetl plik

@ -0,0 +1,66 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.radioconfig
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Forward
import androidx.compose.material.icons.automirrored.filled.Message
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.filled.Cloud
import androidx.compose.material.icons.filled.DataUsage
import androidx.compose.material.icons.filled.LightMode
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.People
import androidx.compose.material.icons.filled.PermScanWifi
import androidx.compose.material.icons.filled.Sensors
import androidx.compose.material.icons.filled.SettingsRemote
import androidx.compose.material.icons.filled.Speed
import androidx.compose.material.icons.filled.Usb
import androidx.compose.ui.graphics.vector.ImageVector
import com.geeksville.mesh.MeshProtos.DeviceMetadata
import com.geeksville.mesh.ui.Route
@Suppress("MagicNumber")
// ModuleConfig (type = AdminProtos.AdminMessage.ModuleConfigType)
enum class ModuleRoute(val title: String, val route: Route, val icon: ImageVector?, val type: Int = 0) {
MQTT("MQTT", Route.MQTT, Icons.Default.Cloud, 0),
SERIAL("Serial", Route.Serial, Icons.Default.Usb, 1),
EXT_NOTIFICATION("External Notification", Route.ExtNotification, Icons.Default.Notifications, 2),
STORE_FORWARD("Store & Forward", Route.StoreForward, Icons.AutoMirrored.Default.Forward, 3),
RANGE_TEST("Range Test", Route.RangeTest, Icons.Default.Speed, 4),
TELEMETRY("Telemetry", Route.Telemetry, Icons.Default.DataUsage, 5),
CANNED_MESSAGE("Canned Message", Route.CannedMessage, Icons.AutoMirrored.Default.Message, 6),
AUDIO("Audio", Route.Audio, Icons.AutoMirrored.Default.VolumeUp, 7),
REMOTE_HARDWARE("Remote Hardware", Route.RemoteHardware, Icons.Default.SettingsRemote, 8),
NEIGHBOR_INFO("Neighbor Info", Route.NeighborInfo, Icons.Default.People, 9),
AMBIENT_LIGHTING("Ambient Lighting", Route.AmbientLighting, Icons.Default.LightMode, 10),
DETECTION_SENSOR("Detection Sensor", Route.DetectionSensor, Icons.Default.Sensors, 11),
PAXCOUNTER("Paxcounter", Route.Paxcounter, Icons.Default.PermScanWifi, 12),
;
val bitfield: Int get() = 1 shl ordinal
companion object {
fun filterExcludedFrom(metadata: DeviceMetadata?): List<ModuleRoute> = entries.filter {
when (metadata) {
null -> true
else -> metadata.excludedModules and it.bitfield == 0
}
}
}
}

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui
package com.geeksville.mesh.ui.radioconfig
import android.app.Activity
import android.content.Intent
@ -65,13 +65,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
import com.geeksville.mesh.R
import com.geeksville.mesh.model.RadioConfigState
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.Route
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.config.EditDeviceProfileDialog
import com.geeksville.mesh.ui.components.config.PacketResponseStateDialog
import com.geeksville.mesh.ui.radioconfig.components.EditDeviceProfileDialog
import com.geeksville.mesh.ui.radioconfig.components.PacketResponseStateDialog
private fun getNavRouteFrom(routeName: String): Any? {
private fun getNavRouteFrom(routeName: String): Route? {
return ConfigRoute.entries.find { it.name == routeName }?.route
?: ModuleRoute.entries.find { it.name == routeName }?.route
}
@ -81,7 +80,7 @@ private fun getNavRouteFrom(routeName: String): Any? {
fun RadioConfigScreen(
viewModel: RadioConfigViewModel = hiltViewModel(),
modifier: Modifier = Modifier,
onNavigate: (Any) -> Unit = {}
onNavigate: (Route) -> Unit = {}
) {
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
var isWaiting by remember { mutableStateOf(false) }
@ -222,54 +221,57 @@ fun NavCard(
}
}
@Suppress("LongMethod")
@Composable
private fun NavButton(@StringRes title: Int, enabled: Boolean, onClick: () -> Unit) {
var showDialog by remember { mutableStateOf(false) }
if (showDialog) AlertDialog(
onDismissRequest = {},
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
title = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
Icon(
imageVector = Icons.TwoTone.Warning,
contentDescription = "warning",
modifier = Modifier.padding(end = 8.dp)
)
Text(
text = "${stringResource(title)}?\n")
Icon(
imageVector = Icons.TwoTone.Warning,
contentDescription = "warning",
modifier = Modifier.padding(start = 8.dp)
)
if (showDialog) {
AlertDialog(
onDismissRequest = {},
shape = RoundedCornerShape(16.dp),
backgroundColor = MaterialTheme.colors.background,
title = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
Icon(
imageVector = Icons.TwoTone.Warning,
contentDescription = "warning",
modifier = Modifier.padding(end = 8.dp)
)
Text(
text = "${stringResource(title)}?\n")
Icon(
imageVector = Icons.TwoTone.Warning,
contentDescription = "warning",
modifier = Modifier.padding(start = 8.dp)
)
}
},
buttons = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(
modifier = Modifier.weight(1f),
onClick = { showDialog = false },
) { Text(stringResource(R.string.cancel)) }
Button(
modifier = Modifier.weight(1f),
onClick = {
showDialog = false
onClick()
},
) { Text(stringResource(R.string.send)) }
}
}
},
buttons = {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(
modifier = Modifier.weight(1f),
onClick = { showDialog = false },
) { Text(stringResource(R.string.cancel)) }
Button(
modifier = Modifier.weight(1f),
onClick = {
showDialog = false
onClick()
},
) { Text(stringResource(R.string.send)) }
}
}
)
)
}
Column {
Spacer(modifier = Modifier.height(4.dp))

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.model
package com.geeksville.mesh.ui.radioconfig
import android.app.Application
import android.net.Uri
@ -39,13 +39,13 @@ import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.config
import com.geeksville.mesh.database.entity.MyNodeEntity
import com.geeksville.mesh.deviceProfile
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.getChannelList
import com.geeksville.mesh.model.getStringResFrom
import com.geeksville.mesh.model.toChannelSet
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.service.MeshService.ConnectionState
import com.geeksville.mesh.ui.AdminRoute
import com.geeksville.mesh.ui.ConfigRoute
import com.geeksville.mesh.ui.ModuleRoute
import com.geeksville.mesh.ui.ResponseState
import com.geeksville.mesh.ui.Route
import com.geeksville.mesh.util.UiText
import com.google.protobuf.MessageLite

Wyświetl plik

@ -0,0 +1,32 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.radioconfig
import com.geeksville.mesh.util.UiText
/**
* Generic sealed class defines each possible state of a response.
*/
sealed class ResponseState<out T> {
data object Empty : 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: UiText) : ResponseState<Nothing>()
fun isWaiting() = this !is Empty
}

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,12 +33,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun AmbientLightingConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,13 +33,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.AudioConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun AudioConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -34,12 +34,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ConfigProtos.Config.BluetoothConfig
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun BluetoothConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -36,13 +36,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.CannedMessageConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun CannedMessageConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.FastOutSlowInEasing
@ -70,12 +70,12 @@ import com.geeksville.mesh.ChannelProtos.ChannelSettings
import com.geeksville.mesh.R
import com.geeksville.mesh.channelSettings
import com.geeksville.mesh.model.Channel
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.dragContainer
import com.geeksville.mesh.ui.components.dragDropItemsIndexed
import com.geeksville.mesh.ui.components.rememberDragDropState
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@OptIn(ExperimentalMaterialApi::class)
@Composable

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -36,13 +36,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun DetectionSensorConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -39,12 +39,12 @@ import com.geeksville.mesh.ConfigProtos.Config.DeviceConfig
import com.geeksville.mesh.R
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
private val DeviceConfig.Role.stringRes: Int
get() = when (this) {

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -34,12 +34,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun DisplayConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -36,13 +36,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.ExternalNotificationConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.components.TextDividerPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun ExternalNotificationConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -37,7 +37,6 @@ import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.Channel
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.model.RegionInfo
import com.geeksville.mesh.model.numChannels
import com.geeksville.mesh.ui.components.DropDownPreference
@ -46,6 +45,7 @@ import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun LoRaConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@ -38,7 +38,6 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.MQTTConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditPasswordPreference
import com.geeksville.mesh.ui.components.EditTextPreference
@ -46,6 +45,7 @@ import com.geeksville.mesh.ui.components.PositionPrecisionPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun MQTTConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -35,12 +35,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos
import com.geeksville.mesh.R
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun NeighborInfoConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.layout.fillMaxSize
@ -46,7 +46,6 @@ import com.geeksville.mesh.ConfigProtos.Config.NetworkConfig
import com.geeksville.mesh.R
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditIPv4Preference
import com.geeksville.mesh.ui.components.EditPasswordPreference
@ -55,6 +54,7 @@ import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SimpleAlertDialog
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Arrangement
@ -37,7 +37,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.R
import com.geeksville.mesh.ui.ResponseState
import com.geeksville.mesh.ui.radioconfig.ResponseState
@Composable
fun <T> PacketResponseStateDialog(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,12 +33,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun PaxcounterConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -36,13 +36,13 @@ import com.geeksville.mesh.ConfigProtos.Config.PositionConfig
import com.geeksville.mesh.Position
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.BitwisePreference
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun PositionConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -34,11 +34,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ConfigProtos.Config.PowerConfig
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun PowerConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,12 +33,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.RangeTestConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun RangeTestConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,12 +33,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.RemoteHardwareConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditListPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun RemoteHardwareConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -34,12 +34,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ConfigProtos.Config.SecurityConfig
import com.geeksville.mesh.config
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.ui.components.EditBase64Preference
import com.geeksville.mesh.ui.components.EditListPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun SecurityConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,13 +33,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.SerialConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.DropDownPreference
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun SerialConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,12 +33,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.StoreForwardConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun StoreForwardConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -33,12 +33,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.TelemetryConfig
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.moduleConfig
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
@Composable
fun TelemetryConfigScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.components.config
package com.geeksville.mesh.ui.radioconfig.components
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@ -36,13 +36,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.copy
import com.geeksville.mesh.model.RadioConfigViewModel
import com.geeksville.mesh.model.getInitials
import com.geeksville.mesh.ui.components.EditTextPreference
import com.geeksville.mesh.ui.components.PreferenceCategory
import com.geeksville.mesh.ui.components.PreferenceFooter
import com.geeksville.mesh.ui.components.RegularPreference
import com.geeksville.mesh.ui.components.SwitchPreference
import com.geeksville.mesh.ui.radioconfig.RadioConfigViewModel
import com.geeksville.mesh.user
@Composable

Wyświetl plik

@ -129,7 +129,6 @@
<ID>FinalNewline:SoftwareUpdateService.kt$com.geeksville.mesh.service.SoftwareUpdateService.kt</ID>
<ID>FinalNewline:SqlTileWriterExt.kt$com.geeksville.mesh.util.SqlTileWriterExt.kt</ID>
<ID>FinalNewline:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt</ID>
<ID>FinalNewline:Theme.kt$com.geeksville.mesh.ui.theme.Theme.kt</ID>
<ID>FinalNewline:UsbBroadcastReceiver.kt$com.geeksville.mesh.repository.usb.UsbBroadcastReceiver.kt</ID>
<ID>FinalNewline:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt</ID>
<ID>ForbiddenComment:MapFragment.kt$// TODO: Accept filename input param from user</ID>
@ -161,7 +160,6 @@
<ID>LongMethod:MainActivity.kt$MainActivity$override fun onOptionsItemSelected(item: MenuItem): Boolean</ID>
<ID>LongMethod:MapFragment.kt$@Composable fun MapView( model: UIViewModel = viewModel(), )</ID>
<ID>LongMethod:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)</ID>
<ID>LongMethod:NetworkConfigItemList.kt$@Composable fun NetworkConfigItemList( networkConfig: NetworkConfig, enabled: Boolean, onSaveClicked: (NetworkConfig) -&gt; Unit, )</ID>
<ID>LongMethod:PowerConfigItemList.kt$@Composable fun PowerConfigItemList( powerConfig: PowerConfig, enabled: Boolean, onSaveClicked: (PowerConfig) -&gt; Unit, )</ID>
<ID>LongMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
<ID>LongMethod:SerialConfigItemList.kt$@Composable fun SerialConfigItemList( serialConfig: SerialConfig, enabled: Boolean, onSaveClicked: (SerialConfig) -&gt; Unit, )</ID>
@ -300,20 +298,6 @@
<ID>MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$360.0</ID>
<ID>MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$4</ID>
<ID>MagicNumber:NOAAWmsTileSource.kt$NOAAWmsTileSource$5</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.BLUETOOTH$6</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.DISPLAY$4</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.LORA$5</ID>
<ID>MagicNumber:NavGraph.kt$ConfigRoute.NETWORK$3</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.AMBIENT_LIGHTING$10</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.AUDIO$7</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.CANNED_MESSAGE$6</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.DETECTION_SENSOR$11</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.NEIGHBOR_INFO$9</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.PAXCOUNTER$12</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.RANGE_TEST$4</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.REMOTE_HARDWARE$8</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.STORE_FORWARD$3</ID>
<ID>MagicNumber:NavGraph.kt$ModuleRoute.TELEMETRY$5</ID>
<ID>MagicNumber:NodeInfo.kt$DeviceMetrics.Companion$1000</ID>
<ID>MagicNumber:NodeInfo.kt$EnvironmentMetrics.Companion$1000</ID>
<ID>MagicNumber:NodeInfo.kt$NodeInfo$0.114</ID>
@ -513,8 +497,6 @@
<ID>MultiLineIfElse:NOAAWmsTileSource.kt$NOAAWmsTileSource$sb.append("service=WMS")</ID>
<ID>MultiLineIfElse:NodeInfo.kt$MeshUser$hwModel.name.replace('_', '-').replace('p', '.').lowercase()</ID>
<ID>MultiLineIfElse:NodeInfo.kt$MeshUser$null</ID>
<ID>MultiLineIfElse:RadioConfigScreen.kt$AlertDialog( onDismissRequest = {}, shape = RoundedCornerShape(16.dp), backgroundColor = MaterialTheme.colors.background, title = { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center, ) { Icon( imageVector = Icons.TwoTone.Warning, contentDescription = "warning", modifier = Modifier.padding(end = 8.dp) ) Text( text = "${stringResource(title)}?\n") Icon( imageVector = Icons.TwoTone.Warning, contentDescription = "warning", modifier = Modifier.padding(start = 8.dp) ) } }, buttons = { Row( modifier = Modifier .fillMaxWidth() .padding(start = 16.dp, end = 16.dp, bottom = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically, ) { TextButton( modifier = Modifier.weight(1f), onClick = { showDialog = false }, ) { Text(stringResource(R.string.cancel)) } Button( modifier = Modifier.weight(1f), onClick = { showDialog = false onClick() }, ) { Text(stringResource(R.string.send)) } } } )</ID>
<ID>MultiLineIfElse:RadioConfigViewModel.kt$RadioConfigViewModel$try { setChannels(channelUrl) } catch (ex: Exception) { errormsg("DeviceProfile channel import error", ex) setResponseStateError(ex.customMessage) }</ID>
<ID>MultiLineIfElse:RadioConfigViewModel.kt$RadioConfigViewModel$viewModelScope.launch { radioConfigRepository.replaceAllSettings(new) }</ID>
<ID>MultiLineIfElse:RadioInterfaceService.kt$RadioInterfaceService$startInterface()</ID>
<ID>MultiLineIfElse:SafeBluetooth.kt$SafeBluetooth$cb</ID>
@ -582,7 +564,6 @@
<ID>NewLineAtEndOfFile:SoftwareUpdateService.kt$com.geeksville.mesh.service.SoftwareUpdateService.kt</ID>
<ID>NewLineAtEndOfFile:SqlTileWriterExt.kt$com.geeksville.mesh.util.SqlTileWriterExt.kt</ID>
<ID>NewLineAtEndOfFile:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt</ID>
<ID>NewLineAtEndOfFile:Theme.kt$com.geeksville.mesh.ui.theme.Theme.kt</ID>
<ID>NewLineAtEndOfFile:UsbBroadcastReceiver.kt$com.geeksville.mesh.repository.usb.UsbBroadcastReceiver.kt</ID>
<ID>NewLineAtEndOfFile:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt</ID>
<ID>NoBlankLineBeforeRbrace:BluetoothInterface.kt$BluetoothInterface$ </ID>
@ -617,8 +598,6 @@
<ID>NoWildcardImports:BluetoothInterface.kt$import com.geeksville.mesh.service.*</ID>
<ID>NoWildcardImports:DeviceVersionTest.kt$import org.junit.Assert.*</ID>
<ID>NoWildcardImports:ExampleUnitTest.kt$import org.junit.Assert.*</ID>
<ID>NoWildcardImports:MeshService.kt$import com.geeksville.mesh.*</ID>
<ID>NoWildcardImports:MeshService.kt$import com.geeksville.mesh.util.*</ID>
<ID>NoWildcardImports:MockInterface.kt$import com.geeksville.mesh.*</ID>
<ID>NoWildcardImports:PreferenceFooter.kt$import androidx.compose.foundation.layout.*</ID>
<ID>NoWildcardImports:PreferenceFooter.kt$import androidx.compose.material.*</ID>
@ -732,8 +711,6 @@
<ID>WildcardImport:BluetoothInterface.kt$import com.geeksville.mesh.service.*</ID>
<ID>WildcardImport:DeviceVersionTest.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:ExampleUnitTest.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:MeshService.kt$import com.geeksville.mesh.*</ID>
<ID>WildcardImport:MeshService.kt$import com.geeksville.mesh.util.*</ID>
<ID>WildcardImport:MockInterface.kt$import com.geeksville.mesh.*</ID>
<ID>WildcardImport:PreferenceFooter.kt$import androidx.compose.foundation.layout.*</ID>
<ID>WildcardImport:PreferenceFooter.kt$import androidx.compose.material.*</ID>