kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
commit
a9c5e65717
|
@ -80,12 +80,12 @@ interface IMeshService {
|
|||
List<DataPacket> getOldMessages();
|
||||
|
||||
/// This method is only intended for use in our GUI, so the user can set radio options
|
||||
/// It returns a RadioConfig protobuf.
|
||||
byte []getRadioConfig();
|
||||
/// It returns a DeviceConfig protobuf.
|
||||
byte []getDeviceConfig();
|
||||
|
||||
/// This method is only intended for use in our GUI, so the user can set radio options
|
||||
/// It sets a RadioConfig protobuf
|
||||
void setRadioConfig(in byte []payload);
|
||||
/// It sets a DeviceConfig protobuf
|
||||
void setDeviceConfig(in byte []payload);
|
||||
|
||||
/// This method is only intended for use in our GUI, so the user can set radio options
|
||||
/// It returns a ChannelSet protobuf.
|
||||
|
|
|
@ -218,9 +218,6 @@ class MainActivity : BaseActivity(), Logging,
|
|||
/** Ask the user to grant Bluetooth scan/discovery permission */
|
||||
fun requestScanPermission() = requestPermission(getScanPermissions(), true)
|
||||
|
||||
/** Ask the user to grant camera permission */
|
||||
fun requestCameraPermission() = requestPermission(getCameraPermissions())
|
||||
|
||||
/** Ask the user to grant foreground location permission */
|
||||
fun requestLocationPermission() = requestPermission(getLocationPermissions())
|
||||
|
||||
|
@ -644,7 +641,7 @@ class MainActivity : BaseActivity(), Logging,
|
|||
|
||||
model.setConnectionState(newConnection)
|
||||
|
||||
debug("Getting latest radioconfig from service")
|
||||
debug("Getting latest DeviceConfig from service")
|
||||
try {
|
||||
val info: MyNodeInfo? = service.myNodeInfo // this can be null
|
||||
model.setMyNodeInfo(info)
|
||||
|
@ -662,9 +659,9 @@ class MainActivity : BaseActivity(), Logging,
|
|||
if (curVer < MeshService.minFirmwareVersion)
|
||||
showAlert(R.string.firmware_too_old, R.string.firmware_old)
|
||||
else {
|
||||
// If our app is too old/new, we probably don't understand the new radioconfig messages, so we don't read them until here
|
||||
// If our app is too old/new, we probably don't understand the new DeviceConfig messages, so we don't read them until here
|
||||
|
||||
model.setRadioConfig(RadioConfigProtos.RadioConfig.parseFrom(service.radioConfig))
|
||||
model.setDeviceConfig(ConfigProtos.Config.parseFrom(service.deviceConfig))
|
||||
|
||||
model.setChannels(ChannelSet(AppOnlyProtos.ChannelSet.parseFrom(service.channels)))
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
|
|||
// TH=he unsecured channel that devices ship with
|
||||
val default = Channel(
|
||||
ChannelProtos.ChannelSettings.newBuilder()
|
||||
.setModemConfig(ChannelProtos.ChannelSettings.ModemConfig.LongFast)
|
||||
// .setModemConfig(ChannelProtos.ChannelSettings.ModemConfig.LongFast)
|
||||
.setPsk(ByteString.copyFrom(defaultPSK))
|
||||
.build()
|
||||
)
|
||||
|
@ -30,26 +30,20 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
|
|||
|
||||
/// Return the name of our channel as a human readable string. If empty string, assume "Default" per mesh.proto spec
|
||||
val name: String
|
||||
get() = if (settings.name.isEmpty()) {
|
||||
// We have a new style 'empty' channel name. Use the same logic from the device to convert that to a human readable name
|
||||
if (settings.bandwidth != 0)
|
||||
"Unset"
|
||||
else when (settings.modemConfig) {
|
||||
ChannelProtos.ChannelSettings.ModemConfig.ShortFast -> "ShortFast"
|
||||
ChannelProtos.ChannelSettings.ModemConfig.ShortSlow -> "ShortSlow"
|
||||
ChannelProtos.ChannelSettings.ModemConfig.MidFast -> "MidFast"
|
||||
ChannelProtos.ChannelSettings.ModemConfig.MidSlow -> "MidSlow"
|
||||
ChannelProtos.ChannelSettings.ModemConfig.LongFast -> "LongFast"
|
||||
ChannelProtos.ChannelSettings.ModemConfig.LongSlow -> "LongSlow"
|
||||
ChannelProtos.ChannelSettings.ModemConfig.VLongSlow -> "VLongSlow"
|
||||
get() = settings.name.ifEmpty { "Placeholder" /*
|
||||
when (settings.modemConfig) {
|
||||
ConfigProtos.Config.LoRaConfig.ModemPreset.ShortFast -> "ShortFast"
|
||||
ConfigProtos.Config.LoRaConfig.ModemPreset.ShortSlow -> "ShortSlow"
|
||||
ConfigProtos.Config.LoRaConfig.ModemPreset.MidFast -> "MidFast"
|
||||
ConfigProtos.Config.LoRaConfig.ModemPreset.MidSlow -> "MidSlow"
|
||||
ConfigProtos.Config.LoRaConfig.ModemPreset.LongFast -> "LongFast"
|
||||
ConfigProtos.Config.LoRaConfig.ModemPreset.LongSlow -> "LongSlow"
|
||||
ConfigProtos.Config.LoRaConfig.ModemPreset.VLongSlow -> "VLongSlow"
|
||||
else -> "Invalid"
|
||||
}
|
||||
} else
|
||||
settings.name
|
||||
}*/
|
||||
}
|
||||
|
||||
val modemConfig: ChannelProtos.ChannelSettings.ModemConfig get() = settings.modemConfig
|
||||
|
||||
val psk
|
||||
val psk: ByteString
|
||||
get() = if (settings.psk.size() != 1)
|
||||
settings.psk // A standard PSK
|
||||
else {
|
||||
|
@ -86,4 +80,4 @@ data class Channel(val settings: ChannelProtos.ChannelSettings) {
|
|||
&& name == o.name
|
||||
}
|
||||
|
||||
fun xorHash(b: ByteArray) = b.fold(0, { acc, x -> acc xor (x.toInt() and 0xff) })
|
||||
fun xorHash(b: ByteArray) = b.fold(0) { acc, x -> acc xor (x.toInt() and 0xff) }
|
|
@ -1,25 +1,25 @@
|
|||
package com.geeksville.mesh.model
|
||||
|
||||
import com.geeksville.mesh.ChannelProtos
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
|
||||
enum class ChannelOption(
|
||||
val modemConfig: ChannelProtos.ChannelSettings.ModemConfig,
|
||||
val modemPreset: ConfigProtos.Config.LoRaConfig.ModemPreset,
|
||||
val configRes: Int,
|
||||
val minBroadcastPeriodSecs: Int
|
||||
) {
|
||||
SHORT_FAST(ChannelProtos.ChannelSettings.ModemConfig.ShortFast,R.string.modem_config_short, 30),
|
||||
SHORT_SLOW(ChannelProtos.ChannelSettings.ModemConfig.ShortSlow, R.string.modem_config_slow_short, 30),
|
||||
MED_FAST(ChannelProtos.ChannelSettings.ModemConfig.MidFast,R.string.modem_config_medium, 60),
|
||||
MED_SLOW(ChannelProtos.ChannelSettings.ModemConfig.MidSlow,R.string.modem_config_slow_medium, 60),
|
||||
LONG_FAST(ChannelProtos.ChannelSettings.ModemConfig.LongFast, R.string.modem_config_long, 240),
|
||||
LONG_SLOW(ChannelProtos.ChannelSettings.ModemConfig.LongSlow, R.string.modem_config_slow_long, 375),
|
||||
VERY_LONG(ChannelProtos.ChannelSettings.ModemConfig.VLongSlow, R.string.modem_config_very_long, 375);
|
||||
SHORT_FAST(ConfigProtos.Config.LoRaConfig.ModemPreset.ShortFast, R.string.modem_config_short, 30),
|
||||
SHORT_SLOW(ConfigProtos.Config.LoRaConfig.ModemPreset.ShortSlow, R.string.modem_config_slow_short, 30),
|
||||
MED_FAST(ConfigProtos.Config.LoRaConfig.ModemPreset.MidFast, R.string.modem_config_medium, 60),
|
||||
MED_SLOW(ConfigProtos.Config.LoRaConfig.ModemPreset.MidSlow, R.string.modem_config_slow_medium, 60),
|
||||
LONG_FAST(ConfigProtos.Config.LoRaConfig.ModemPreset.LongFast, R.string.modem_config_long, 240),
|
||||
LONG_SLOW(ConfigProtos.Config.LoRaConfig.ModemPreset.LongSlow, R.string.modem_config_slow_long, 375),
|
||||
VERY_LONG(ConfigProtos.Config.LoRaConfig.ModemPreset.VLongSlow, R.string.modem_config_very_long, 375);
|
||||
|
||||
companion object {
|
||||
fun fromConfig(modemConfig: ChannelProtos.ChannelSettings.ModemConfig?): ChannelOption? {
|
||||
fun fromConfig(modemPreset: ConfigProtos.Config.LoRaConfig.ModemPreset?): ChannelOption? {
|
||||
for (option in values()) {
|
||||
if (option.modemConfig == modemConfig)
|
||||
if (option.modemPreset == modemPreset)
|
||||
return option
|
||||
}
|
||||
return null
|
||||
|
|
|
@ -22,8 +22,6 @@ data class ChannelSet(
|
|||
private fun urlToChannels(url: Uri): AppOnlyProtos.ChannelSet {
|
||||
val urlStr = url.toString()
|
||||
|
||||
// We no longer support the super old (about 0.8ish? verison of the URLs that don't use the # separator - I doubt
|
||||
// anyone is still using that old format
|
||||
val pathRegex = Regex("$prefix(.*)", RegexOption.IGNORE_CASE)
|
||||
val (base64) = pathRegex.find(urlStr)?.destructured
|
||||
?: throw MalformedURLException("Not a meshtastic URL: ${urlStr.take(40)}")
|
||||
|
@ -51,13 +49,13 @@ data class ChannelSet(
|
|||
/// Return an URL that represents the current channel values
|
||||
/// @param upperCasePrefix - portions of the URL can be upper case to make for more efficient QR codes
|
||||
fun getChannelUrl(upperCasePrefix: Boolean = false): Uri {
|
||||
// If we have a valid radio config use it, othterwise use whatever we have saved in the prefs
|
||||
// If we have a valid radio config use it, otherwise use whatever we have saved in the prefs
|
||||
|
||||
val channelBytes = protobuf.toByteArray() ?: ByteArray(0) // if unset just use empty
|
||||
val enc = Base64.encodeToString(channelBytes, base64Flags)
|
||||
|
||||
val p = if (upperCasePrefix)
|
||||
prefix.toUpperCase()
|
||||
prefix.uppercase()
|
||||
else
|
||||
prefix
|
||||
return Uri.parse("$p$enc")
|
||||
|
|
|
@ -43,8 +43,8 @@ fun getInitials(nameIn: String): String {
|
|||
val words = name.split(Regex("\\s+")).filter { it.isNotEmpty() }
|
||||
|
||||
val initials = when (words.size) {
|
||||
in 0..minchars - 1 -> {
|
||||
val nm = if (name.length >= 1)
|
||||
in 0 until minchars -> {
|
||||
val nm = if (name.isNotEmpty())
|
||||
name.first() + name.drop(1).filterNot { c -> c.lowercase() in "aeiou" }
|
||||
else
|
||||
""
|
||||
|
@ -101,8 +101,8 @@ class UIViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
/// various radio settings (including the channel)
|
||||
private val _radioConfig = MutableLiveData<RadioConfigProtos.RadioConfig?>()
|
||||
val radioConfig: LiveData<RadioConfigProtos.RadioConfig?> get() = _radioConfig
|
||||
private val _deviceConfig = MutableLiveData<ConfigProtos.Config?>()
|
||||
val deviceConfig: LiveData<ConfigProtos.Config?> get() = _deviceConfig
|
||||
|
||||
private val _channels = MutableLiveData<ChannelSet?>()
|
||||
val channels: LiveData<ChannelSet?> get() = _channels
|
||||
|
@ -123,7 +123,7 @@ class UIViewModel @Inject constructor(
|
|||
|
||||
var positionBroadcastSecs: Int?
|
||||
get() {
|
||||
_radioConfig.value?.preferences?.let {
|
||||
_deviceConfig.value?.position?.let {
|
||||
if (it.positionBroadcastSecs > 0) return it.positionBroadcastSecs
|
||||
// These default values are borrowed from the device code.
|
||||
return 15 * 60
|
||||
|
@ -131,50 +131,50 @@ class UIViewModel @Inject constructor(
|
|||
return null
|
||||
}
|
||||
set(value) {
|
||||
val config = _radioConfig.value
|
||||
val config = _deviceConfig.value
|
||||
if (value != null && config != null) {
|
||||
val builder = config.toBuilder()
|
||||
builder.preferencesBuilder.positionBroadcastSecs = value
|
||||
setRadioConfig(builder.build())
|
||||
builder.positionBuilder.positionBroadcastSecs = value
|
||||
setDeviceConfig(builder.build())
|
||||
}
|
||||
}
|
||||
|
||||
var lsSleepSecs: Int?
|
||||
get() = _radioConfig.value?.preferences?.lsSecs
|
||||
get() = _deviceConfig.value?.power?.lsSecs
|
||||
set(value) {
|
||||
val config = _radioConfig.value
|
||||
val config = _deviceConfig.value
|
||||
if (value != null && config != null) {
|
||||
val builder = config.toBuilder()
|
||||
builder.preferencesBuilder.lsSecs = value
|
||||
setRadioConfig(builder.build())
|
||||
builder.powerBuilder.lsSecs = value
|
||||
setDeviceConfig(builder.build())
|
||||
}
|
||||
}
|
||||
|
||||
var locationShareDisabled: Boolean
|
||||
get() = _radioConfig.value?.preferences?.locationShareDisabled ?: false
|
||||
var gpsDisabled: Boolean
|
||||
get() = _deviceConfig.value?.position?.gpsDisabled ?: false
|
||||
set(value) {
|
||||
val config = _radioConfig.value
|
||||
val config = _deviceConfig.value
|
||||
if (config != null) {
|
||||
val builder = config.toBuilder()
|
||||
builder.preferencesBuilder.locationShareDisabled = value
|
||||
setRadioConfig(builder.build())
|
||||
builder.positionBuilder.gpsDisabled = value
|
||||
setDeviceConfig(builder.build())
|
||||
}
|
||||
}
|
||||
|
||||
var isPowerSaving: Boolean?
|
||||
get() = _radioConfig.value?.preferences?.isPowerSaving
|
||||
get() = _deviceConfig.value?.power?.isPowerSaving
|
||||
set(value) {
|
||||
val config = _radioConfig.value
|
||||
val config = _deviceConfig.value
|
||||
if (value != null && config != null) {
|
||||
val builder = config.toBuilder()
|
||||
builder.preferencesBuilder.isPowerSaving = value
|
||||
setRadioConfig(builder.build())
|
||||
builder.powerBuilder.isPowerSaving = value
|
||||
setDeviceConfig(builder.build())
|
||||
}
|
||||
}
|
||||
|
||||
var region: RadioConfigProtos.RegionCode
|
||||
get() = meshService?.region?.let { RadioConfigProtos.RegionCode.forNumber(it) }
|
||||
?: RadioConfigProtos.RegionCode.Unset
|
||||
var region: ConfigProtos.Config.LoRaConfig.RegionCode
|
||||
get() = meshService?.region?.let { ConfigProtos.Config.LoRaConfig.RegionCode.forNumber(it) }
|
||||
?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
|
||||
set(value) {
|
||||
meshService?.region = value.number
|
||||
}
|
||||
|
@ -216,10 +216,10 @@ class UIViewModel @Inject constructor(
|
|||
val primaryChannel: Channel? get() = _channels.value?.primaryChannel
|
||||
|
||||
// Set the radio config (also updates our saved copy in preferences)
|
||||
fun setRadioConfig(c: RadioConfigProtos.RadioConfig) {
|
||||
fun setDeviceConfig(c: ConfigProtos.Config) {
|
||||
debug("Setting new radio config!")
|
||||
meshService?.radioConfig = c.toByteArray()
|
||||
_radioConfig.value =
|
||||
meshService?.deviceConfig = c.toByteArray()
|
||||
_deviceConfig.value =
|
||||
c // Must be done after calling the service, so we will will properly throw if the service failed (and therefore not cache invalid new settings)
|
||||
}
|
||||
|
||||
|
|
|
@ -60,13 +60,13 @@ class MockInterface(private val service: RadioInterfaceService) : Logging, IRadi
|
|||
|
||||
private fun handleAdminPacket(pr: MeshProtos.ToRadio, d: AdminProtos.AdminMessage) {
|
||||
when {
|
||||
d.getRadioRequest ->
|
||||
d.getConfigRequest == AdminProtos.AdminMessage.ConfigType.LORA_CONFIG ->
|
||||
sendAdmin(pr.packet.to, pr.packet.from, pr.packet.id) {
|
||||
getRadioResponse = RadioConfigProtos.RadioConfig.newBuilder().apply {
|
||||
getConfigResponse = ConfigProtos.Config.newBuilder().apply {
|
||||
|
||||
preferences =
|
||||
RadioConfigProtos.RadioConfig.UserPreferences.newBuilder().apply {
|
||||
region = RadioConfigProtos.RegionCode.TW
|
||||
lora =
|
||||
ConfigProtos.Config.LoRaConfig.newBuilder().apply {
|
||||
region = ConfigProtos.Config.LoRaConfig.RegionCode.TW
|
||||
// FIXME set critical times?
|
||||
}.build()
|
||||
}.build()
|
||||
|
|
|
@ -83,7 +83,7 @@ class MeshService : Service(), Logging {
|
|||
class NodeNumNotFoundException(id: Int) : NodeNotFoundException("NodeNum not found $id")
|
||||
class IdNotFoundException(id: String) : NodeNotFoundException("ID not found $id")
|
||||
|
||||
class NoRadioConfigException(message: String = "No radio settings received (is our app too old?)") :
|
||||
class NoDeviceConfigException(message: String = "No radio settings received (is our app too old?)") :
|
||||
RadioNotConnectedException(message)
|
||||
|
||||
/** We treat software update as similar to loss of comms to the regular bluetooth service (so things like sendPosition for background GPS ignores the problem */
|
||||
|
@ -128,7 +128,7 @@ class MeshService : Service(), Logging {
|
|||
private var locationFlow: Job? = null
|
||||
|
||||
// If we've ever read a valid region code from our device it will be here
|
||||
var curRegionValue = RadioConfigProtos.RegionCode.Unset_VALUE
|
||||
var curRegionValue = ConfigProtos.Config.LoRaConfig.RegionCode.Unset_VALUE
|
||||
|
||||
private fun getSenderName(packet: DataPacket?): String {
|
||||
val name = nodeDBbyID[packet?.from]?.user?.longName
|
||||
|
@ -350,7 +350,7 @@ class MeshService : Service(), Logging {
|
|||
|
||||
var myNodeInfo: MyNodeInfo? = null
|
||||
|
||||
private var radioConfig: RadioConfigProtos.RadioConfig? = null
|
||||
private var deviceConfig: ConfigProtos.Config? = null
|
||||
|
||||
private var channels = fixupChannelList(listOf())
|
||||
|
||||
|
@ -725,9 +725,9 @@ class MeshService : Service(), Logging {
|
|||
// For the time being we only care about admin messages from our local node
|
||||
if (fromNodeNum == myNodeNum) {
|
||||
when (a.variantCase) {
|
||||
AdminProtos.AdminMessage.VariantCase.GET_RADIO_RESPONSE -> {
|
||||
debug("Admin: received radioConfig")
|
||||
radioConfig = a.getRadioResponse
|
||||
AdminProtos.AdminMessage.VariantCase.GET_CONFIG_RESPONSE -> {
|
||||
debug("Admin: received deviceConfig")
|
||||
deviceConfig = a.getConfigResponse
|
||||
requestChannel(0) // Now start reading channels
|
||||
}
|
||||
|
||||
|
@ -983,7 +983,7 @@ class MeshService : Service(), Logging {
|
|||
sleepTimeout = serviceScope.handledLaunch {
|
||||
try {
|
||||
// If we have a valid timeout, wait that long (+30 seconds) otherwise, just wait 30 seconds
|
||||
val timeout = (radioConfig?.preferences?.lsSecs ?: 0) + 30
|
||||
val timeout = (deviceConfig?.power?.lsSecs ?: 0) + 30
|
||||
|
||||
debug("Waiting for sleeping device, timeout=$timeout secs")
|
||||
delay(timeout * 1000L)
|
||||
|
@ -1075,7 +1075,7 @@ class MeshService : Service(), Logging {
|
|||
|
||||
private fun onRadioConnectionState(state: RadioServiceConnectionState) {
|
||||
// sleep now disabled by default on ESP32, permanent is true unless isPowerSaving enabled
|
||||
val lsEnabled = radioConfig?.preferences?.isPowerSaving ?: false
|
||||
val lsEnabled = deviceConfig?.power?.isPowerSaving ?: false
|
||||
val connected = state.isConnected
|
||||
val permanent = state.isPermanent || !lsEnabled
|
||||
onConnectionChanged(
|
||||
|
@ -1105,7 +1105,7 @@ class MeshService : Service(), Logging {
|
|||
|
||||
MeshProtos.FromRadio.NODE_INFO_FIELD_NUMBER -> handleNodeInfo(proto.nodeInfo)
|
||||
|
||||
// MeshProtos.FromRadio.RADIO_FIELD_NUMBER -> handleRadioConfig(proto.radio)
|
||||
// MeshProtos.FromRadio.RADIO_FIELD_NUMBER -> handleDeviceConfig(proto.radio)
|
||||
|
||||
else -> errormsg("Unexpected FromRadio variant")
|
||||
}
|
||||
|
@ -1249,7 +1249,7 @@ class MeshService : Service(), Logging {
|
|||
regenMyNodeInfo()
|
||||
|
||||
// We'll need to get a new set of channels and settings now
|
||||
radioConfig = null
|
||||
deviceConfig = null
|
||||
|
||||
// prefill the channel array with null channels
|
||||
channels = fixupChannelList(listOf<ChannelProtos.Channel>())
|
||||
|
@ -1272,21 +1272,21 @@ class MeshService : Service(), Logging {
|
|||
|
||||
private fun setRegionOnDevice() {
|
||||
val curConfigRegion =
|
||||
radioConfig?.preferences?.region ?: RadioConfigProtos.RegionCode.Unset
|
||||
deviceConfig?.lora?.region ?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
|
||||
|
||||
if (curConfigRegion.number != curRegionValue && curRegionValue != RadioConfigProtos.RegionCode.Unset_VALUE)
|
||||
if (curConfigRegion.number != curRegionValue && curRegionValue != ConfigProtos.Config.LoRaConfig.RegionCode.Unset_VALUE)
|
||||
if (deviceVersion >= minFirmwareVersion) {
|
||||
info("Telling device to upgrade region")
|
||||
|
||||
// Tell the device to set the new region field (old devices will simply ignore this)
|
||||
radioConfig?.let { currentConfig ->
|
||||
deviceConfig?.let { currentConfig ->
|
||||
val newConfig = currentConfig.toBuilder()
|
||||
|
||||
val newPrefs = currentConfig.preferences.toBuilder()
|
||||
val newPrefs = currentConfig.lora.toBuilder()
|
||||
newPrefs.regionValue = curRegionValue
|
||||
newConfig.preferences = newPrefs.build()
|
||||
newConfig.lora = newPrefs.build()
|
||||
|
||||
sendRadioConfig(newConfig.build())
|
||||
sendDeviceConfig(newConfig.build())
|
||||
}
|
||||
} else
|
||||
warn("Device is too old to understand region changes")
|
||||
|
@ -1301,8 +1301,8 @@ class MeshService : Service(), Logging {
|
|||
// Try to pull our region code from the new preferences field
|
||||
// FIXME - do not check net - figuring out why board is rebooting
|
||||
val curConfigRegion =
|
||||
radioConfig?.preferences?.region ?: RadioConfigProtos.RegionCode.Unset
|
||||
if (curConfigRegion != RadioConfigProtos.RegionCode.Unset) {
|
||||
deviceConfig?.lora?.region ?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
|
||||
if (curConfigRegion != ConfigProtos.Config.LoRaConfig.RegionCode.Unset) {
|
||||
info("Using device region $curConfigRegion (code ${curConfigRegion.number})")
|
||||
curRegionValue = curConfigRegion.number
|
||||
}
|
||||
|
@ -1357,15 +1357,15 @@ class MeshService : Service(), Logging {
|
|||
info("Device firmware is too old, faking config so firmware update can occur")
|
||||
onHasSettings()
|
||||
} else
|
||||
requestRadioConfig()
|
||||
requestDeviceConfig()
|
||||
}
|
||||
} else
|
||||
warn("Ignoring stale config complete")
|
||||
}
|
||||
|
||||
private fun requestRadioConfig() {
|
||||
private fun requestDeviceConfig() {
|
||||
sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket(wantResponse = true) {
|
||||
getRadioRequest = true
|
||||
getConfigRequest = AdminProtos.AdminMessage.ConfigType.DEVICE_CONFIG
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1440,22 +1440,22 @@ class MeshService : Service(), Logging {
|
|||
|
||||
/** Send our current radio config to the device
|
||||
*/
|
||||
private fun sendRadioConfig(c: RadioConfigProtos.RadioConfig) {
|
||||
private fun sendDeviceConfig(c: ConfigProtos.Config) {
|
||||
// send the packet into the mesh
|
||||
sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket {
|
||||
setRadio = c
|
||||
setConfig = c
|
||||
})
|
||||
|
||||
// Update our cached copy
|
||||
this@MeshService.radioConfig = c
|
||||
this@MeshService.deviceConfig = c
|
||||
}
|
||||
|
||||
/** Set our radio config
|
||||
*/
|
||||
private fun setRadioConfig(payload: ByteArray) {
|
||||
val parsed = RadioConfigProtos.RadioConfig.parseFrom(payload)
|
||||
private fun setDeviceConfig(payload: ByteArray) {
|
||||
val parsed = ConfigProtos.Config.parseFrom(payload)
|
||||
|
||||
sendRadioConfig(parsed)
|
||||
sendDeviceConfig(parsed)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1690,13 +1690,13 @@ class MeshService : Service(), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
override fun getRadioConfig(): ByteArray = toRemoteExceptions {
|
||||
this@MeshService.radioConfig?.toByteArray()
|
||||
?: throw NoRadioConfigException()
|
||||
override fun getDeviceConfig(): ByteArray = toRemoteExceptions {
|
||||
this@MeshService.deviceConfig?.toByteArray()
|
||||
?: throw NoDeviceConfigException()
|
||||
}
|
||||
|
||||
override fun setRadioConfig(payload: ByteArray) = toRemoteExceptions {
|
||||
this@MeshService.setRadioConfig(payload)
|
||||
override fun setDeviceConfig(payload: ByteArray) = toRemoteExceptions {
|
||||
this@MeshService.setDeviceConfig(payload)
|
||||
}
|
||||
|
||||
override fun getChannels(): ByteArray = toRemoteExceptions {
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.geeksville.mesh.service
|
|||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.MyNodeInfo
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.RadioConfigProtos
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/// Our saved preferences as stored on disk
|
||||
|
@ -12,7 +12,7 @@ data class MeshServiceSettingsData(
|
|||
val nodeDB: Array<NodeInfo>,
|
||||
val myInfo: MyNodeInfo,
|
||||
val messages: Array<DataPacket>,
|
||||
val regionCode: Int = RadioConfigProtos.RegionCode.Unset_VALUE
|
||||
val regionCode: Int = ConfigProtos.Config.LoRaConfig.RegionCode.Unset_VALUE
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
|
|
|
@ -37,18 +37,18 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
model.radioConfig.observe(viewLifecycleOwner) {
|
||||
model.deviceConfig.observe(viewLifecycleOwner) {
|
||||
binding.positionBroadcastPeriodEditText.setText(model.positionBroadcastSecs.toString())
|
||||
binding.lsSleepEditText.setText(model.lsSleepSecs.toString())
|
||||
binding.positionBroadcastPeriodView.isEnabled = !model.locationShareDisabled
|
||||
binding.positionBroadcastSwitch.isChecked = !model.locationShareDisabled
|
||||
binding.positionBroadcastPeriodView.isEnabled = !model.gpsDisabled
|
||||
binding.positionBroadcastSwitch.isChecked = !model.gpsDisabled
|
||||
binding.lsSleepView.isEnabled = model.isPowerSaving ?: false
|
||||
binding.lsSleepSwitch.isChecked = model.isPowerSaving ?: false
|
||||
}
|
||||
|
||||
model.connectionState.observe(viewLifecycleOwner) { connectionState ->
|
||||
val connected = connectionState == MeshService.ConnectionState.CONNECTED
|
||||
binding.positionBroadcastPeriodView.isEnabled = connected && !model.locationShareDisabled
|
||||
binding.positionBroadcastPeriodView.isEnabled = connected && !model.gpsDisabled
|
||||
binding.lsSleepView.isEnabled = connected && model.isPowerSaving ?: false
|
||||
binding.positionBroadcastSwitch.isEnabled = connected
|
||||
binding.lsSleepSwitch.isEnabled = connected
|
||||
|
@ -58,7 +58,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
|||
val textEdit = binding.positionBroadcastPeriodEditText
|
||||
val n = textEdit.text.toString().toIntOrNull()
|
||||
val minBroadcastPeriodSecs =
|
||||
ChannelOption.fromConfig(model.primaryChannel?.modemConfig)?.minBroadcastPeriodSecs
|
||||
ChannelOption.fromConfig(model.deviceConfig.value?.lora?.modemPreset)?.minBroadcastPeriodSecs
|
||||
?: ChannelOption.defaultMinBroadcastPeriod
|
||||
|
||||
if (n != null && n < MAX_INT_DEVICE && (n == 0 || n >= minBroadcastPeriodSecs)) {
|
||||
|
@ -81,7 +81,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
|||
|
||||
binding.positionBroadcastSwitch.setOnCheckedChangeListener { view, isChecked ->
|
||||
if (view.isPressed) {
|
||||
model.locationShareDisabled = !isChecked
|
||||
model.gpsDisabled = !isChecked
|
||||
debug("User changed locationShare to $isChecked")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.geeksville.android.hideKeyboard
|
|||
import com.geeksville.android.isGooglePlayAvailable
|
||||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.ChannelProtos
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.hasCameraPermission
|
||||
import com.geeksville.mesh.databinding.ChannelFragmentBinding
|
||||
|
@ -107,15 +108,15 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
binding.channelNameEdit.setText(channel.humanName)
|
||||
|
||||
// For now, we only let the user edit/save channels while the radio is awake - because the service
|
||||
// doesn't cache radioconfig writes.
|
||||
// doesn't cache DeviceConfig writes.
|
||||
binding.editableCheckbox.isEnabled = connected
|
||||
|
||||
val bitmap = channels.qrCode
|
||||
if (bitmap != null)
|
||||
binding.qrView.setImageBitmap(bitmap)
|
||||
|
||||
val modemConfig = channel.modemConfig
|
||||
val channelOption = ChannelOption.fromConfig(modemConfig)
|
||||
val modemPreset = model.deviceConfig.value?.lora?.modemPreset
|
||||
val channelOption = ChannelOption.fromConfig(modemPreset)
|
||||
binding.filledExposedDropdown.setText(
|
||||
getString(
|
||||
channelOption?.configRes ?: R.string.modem_config_unrecognized
|
||||
|
@ -128,12 +129,12 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
binding.editableCheckbox.isEnabled = false
|
||||
}
|
||||
|
||||
val modemConfigs = ChannelOption.values()
|
||||
val modemConfigList = modemConfigs.map { getString(it.configRes) }
|
||||
val modemPresets = ChannelOption.values()
|
||||
val modemPresetList = modemPresets.map { getString(it.configRes) }
|
||||
val adapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
R.layout.dropdown_menu_popup_item,
|
||||
modemConfigList
|
||||
modemPresetList
|
||||
)
|
||||
|
||||
binding.filledExposedDropdown.setAdapter(adapter)
|
||||
|
@ -174,10 +175,10 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
private fun installSettings(newChannel: ChannelProtos.ChannelSettings) {
|
||||
val newSet =
|
||||
ChannelSet(AppOnlyProtos.ChannelSet.newBuilder().addSettings(newChannel).build())
|
||||
// Try to change the radio, if it fails, tell the user why and throw away their redits
|
||||
// Try to change the radio, if it fails, tell the user why and throw away their edits
|
||||
try {
|
||||
model.setChannels(newSet)
|
||||
// Since we are writing to radioconfig, that will trigger the rest of the GUI update (QR code etc)
|
||||
// Since we are writing to DeviceConfig, that will trigger the rest of the GUI update (QR code etc)
|
||||
} catch (ex: RemoteException) {
|
||||
errormsg("ignoring channel problem", ex)
|
||||
|
||||
|
@ -282,7 +283,7 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
if (checked) {
|
||||
// User just unlocked for editing - remove the # goo around the channel name
|
||||
model.channels.value?.primaryChannel?.let { ch ->
|
||||
// Note: We are careful to show the emptystring here if the user was on a default channel, so the user knows they should it for any changes
|
||||
// Note: We are careful to show the empty string here if the user was on a default channel, so the user knows they should it for any changes
|
||||
originalName = ch.settings.name
|
||||
binding.channelNameEdit.setText(originalName)
|
||||
}
|
||||
|
@ -303,18 +304,20 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
// Find the new modem config
|
||||
val selectedChannelOptionString =
|
||||
binding.filledExposedDropdown.editableText.toString()
|
||||
var modemConfig = getModemConfig(selectedChannelOptionString)
|
||||
if (modemConfig == ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED) // Huh? didn't find it - keep same
|
||||
modemConfig = oldPrimary.settings.modemConfig
|
||||
var modemPreset = getModemPreset(selectedChannelOptionString)
|
||||
// if (modemPreset == ConfigProtos.Config.LoRaConfig.ModemPreset.UNRECOGNIZED) // Huh? didn't find it - keep same
|
||||
// modemPreset = oldPrimary.settings.modemConfig -> TODO add from LoraConfig.ModemPreset?
|
||||
|
||||
// Generate a new AES256 key if the user changes channel name or the name is non-default and the settings changed
|
||||
if (newName != originalName || (newName.isNotEmpty() && modemConfig != oldPrimary.settings.modemConfig)) {
|
||||
// if (newName != originalName || (newName.isNotEmpty() && modemConfig != oldPrimary.settings.modemConfig)) {
|
||||
if (newName != originalName || newName.isNotEmpty()) {
|
||||
|
||||
// Install a new customized channel
|
||||
debug("ASSIGNING NEW AES256 KEY")
|
||||
val random = SecureRandom()
|
||||
val bytes = ByteArray(32)
|
||||
random.nextBytes(bytes)
|
||||
newSettings.name = newName
|
||||
newSettings.name = newName.take(11) // proto max_size:12
|
||||
newSettings.psk = ByteString.copyFrom(bytes)
|
||||
} else {
|
||||
debug("Switching back to default channel")
|
||||
|
@ -322,7 +325,7 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
}
|
||||
|
||||
// No matter what apply the speed selection from the user
|
||||
newSettings.modemConfig = modemConfig
|
||||
// newSettings.modemConfig = modemPreset -> TODO add from LoraConfig.ModemPreset?
|
||||
|
||||
installSettings(newSettings.build())
|
||||
}
|
||||
|
@ -348,12 +351,12 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getModemConfig(selectedChannelOptionString: String): ChannelProtos.ChannelSettings.ModemConfig {
|
||||
private fun getModemPreset(selectedChannelOptionString: String): ConfigProtos.Config.LoRaConfig.ModemPreset {
|
||||
for (item in ChannelOption.values()) {
|
||||
if (getString(item.configRes) == selectedChannelOptionString)
|
||||
return item.modemConfig
|
||||
return item.modemPreset
|
||||
}
|
||||
return ChannelProtos.ChannelSettings.ModemConfig.UNRECOGNIZED
|
||||
return ConfigProtos.Config.LoRaConfig.ModemPreset.UNRECOGNIZED
|
||||
}
|
||||
|
||||
private val requestPermissionAndScanLauncher =
|
||||
|
|
|
@ -87,7 +87,7 @@ class ContactsFragment : ScreenFragment("Messages"), Logging {
|
|||
val nodes = model.nodeDB.nodes.value!!
|
||||
val node = nodes[if (isLocal) contact.to else contact.from]
|
||||
|
||||
//grab channel names from RadioConfig
|
||||
//grab channel names from DeviceConfig
|
||||
val channels = model.channels.value
|
||||
val primaryChannel = channels?.primaryChannel
|
||||
|
||||
|
|
|
@ -296,9 +296,6 @@ class MessagesFragment : Fragment(), Logging {
|
|||
val connected = connectionState == MeshService.ConnectionState.CONNECTED
|
||||
binding.textInputLayout.isEnabled = connected
|
||||
binding.sendButton.isEnabled = connected
|
||||
|
||||
// Just being connected is enough to allow sending texts I think
|
||||
// && model.nodeDB.myId.value != null && model.radioConfig.value != null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import com.geeksville.android.hideKeyboard
|
|||
import com.geeksville.android.isGooglePlayAvailable
|
||||
import com.geeksville.mesh.MainActivity
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.RadioConfigProtos
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.android.*
|
||||
import com.geeksville.mesh.databinding.SettingsFragmentBinding
|
||||
import com.geeksville.mesh.model.BluetoothViewModel
|
||||
|
@ -659,7 +659,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
// update the region selection from the device
|
||||
val region = model.region
|
||||
val spinner = binding.regionSpinner
|
||||
val unsetIndex = regions.indexOf(RadioConfigProtos.RegionCode.Unset.name)
|
||||
val unsetIndex = regions.indexOf(ConfigProtos.Config.LoRaConfig.RegionCode.Unset.name)
|
||||
spinner.onItemSelectedListener = null
|
||||
|
||||
debug("current region is $region")
|
||||
|
@ -673,7 +673,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
spinner.isEnabled = true
|
||||
|
||||
// If actively connected possibly let the user update firmware
|
||||
refreshUpdateButton(region != RadioConfigProtos.RegionCode.Unset)
|
||||
refreshUpdateButton(region != ConfigProtos.Config.LoRaConfig.RegionCode.Unset)
|
||||
|
||||
// Update the status string (highest priority messages first)
|
||||
val info = model.myNodeInfo.value
|
||||
|
@ -683,7 +683,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
(permissionsWarning != null) ->
|
||||
statusText.text = permissionsWarning
|
||||
|
||||
region == RadioConfigProtos.RegionCode.Unset ->
|
||||
region == ConfigProtos.Config.LoRaConfig.RegionCode.Unset ->
|
||||
statusText.text = getString(R.string.must_set_region)
|
||||
|
||||
connected == MeshService.ConnectionState.CONNECTED -> {
|
||||
|
@ -705,7 +705,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
id: Long
|
||||
) {
|
||||
val item = parent.getItemAtPosition(position) as String?
|
||||
val asProto = item!!.let { RadioConfigProtos.RegionCode.valueOf(it) }
|
||||
val asProto = item!!.let { ConfigProtos.Config.LoRaConfig.RegionCode.valueOf(it) }
|
||||
exceptionToSnackbar(requireView()) {
|
||||
model.region = asProto
|
||||
}
|
||||
|
@ -718,8 +718,8 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
}
|
||||
|
||||
/// the sorted list of region names like arrayOf("US", "CN", "EU488")
|
||||
private val regions = RadioConfigProtos.RegionCode.values().filter {
|
||||
it != RadioConfigProtos.RegionCode.UNRECOGNIZED
|
||||
private val regions = ConfigProtos.Config.LoRaConfig.RegionCode.values().filter {
|
||||
it != ConfigProtos.Config.LoRaConfig.RegionCode.UNRECOGNIZED
|
||||
}.map {
|
||||
it.name
|
||||
}.sorted()
|
||||
|
@ -746,10 +746,10 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
updateDevicesButtons(scanModel.devices.value)
|
||||
}
|
||||
|
||||
model.radioConfig.observe(viewLifecycleOwner) {
|
||||
model.deviceConfig.observe(viewLifecycleOwner) {
|
||||
binding.provideLocationCheckbox.isEnabled =
|
||||
isGooglePlayAvailable(requireContext()) && !model.locationShareDisabled
|
||||
if (model.locationShareDisabled) {
|
||||
isGooglePlayAvailable(requireContext()) && !model.gpsDisabled
|
||||
if (model.gpsDisabled) {
|
||||
model.provideLocation.value = false
|
||||
binding.provideLocationCheckbox.isChecked = false
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="64dp"
|
||||
android:hint="@string/channel_name"
|
||||
app:counterEnabled="true"
|
||||
app:counterMaxLength="11"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
@ -23,7 +25,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890- "
|
||||
android:imeOptions="actionDone"
|
||||
android:maxLength="15"
|
||||
android:singleLine="true"
|
||||
android:text="@string/unset" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
|
Ładowanie…
Reference in New Issue