mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor config get and set methods
This commit is contained in:
parent
346a50a360
commit
fd0c8ef9b8
4 changed files with 72 additions and 118 deletions
|
|
@ -13,10 +13,12 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.geeksville.mesh.android.Logging
|
import com.geeksville.mesh.android.Logging
|
||||||
import com.geeksville.mesh.*
|
import com.geeksville.mesh.*
|
||||||
|
import com.geeksville.mesh.ConfigProtos.Config
|
||||||
import com.geeksville.mesh.database.PacketRepository
|
import com.geeksville.mesh.database.PacketRepository
|
||||||
import com.geeksville.mesh.database.QuickChatActionRepository
|
import com.geeksville.mesh.database.QuickChatActionRepository
|
||||||
import com.geeksville.mesh.database.entity.Packet
|
import com.geeksville.mesh.database.entity.Packet
|
||||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||||
|
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
||||||
import com.geeksville.mesh.repository.datastore.LocalConfigRepository
|
import com.geeksville.mesh.repository.datastore.LocalConfigRepository
|
||||||
import com.geeksville.mesh.service.MeshService
|
import com.geeksville.mesh.service.MeshService
|
||||||
import com.geeksville.mesh.util.GPSFormat
|
import com.geeksville.mesh.util.GPSFormat
|
||||||
|
|
@ -71,8 +73,9 @@ class UIViewModel @Inject constructor(
|
||||||
private val _allPacketState = MutableStateFlow<List<Packet>>(emptyList())
|
private val _allPacketState = MutableStateFlow<List<Packet>>(emptyList())
|
||||||
val allPackets: StateFlow<List<Packet>> = _allPacketState
|
val allPackets: StateFlow<List<Packet>> = _allPacketState
|
||||||
|
|
||||||
private val _localConfig = MutableLiveData<LocalOnlyProtos.LocalConfig>()
|
private val _localConfig = MutableStateFlow<LocalConfig>(LocalConfig.getDefaultInstance())
|
||||||
val localConfig: LiveData<LocalOnlyProtos.LocalConfig> get() = _localConfig
|
val localConfig: StateFlow<LocalConfig> = _localConfig
|
||||||
|
val config get() = _localConfig.value
|
||||||
|
|
||||||
private val _quickChatActions = MutableStateFlow<List<QuickChatAction>>(emptyList())
|
private val _quickChatActions = MutableStateFlow<List<QuickChatAction>>(emptyList())
|
||||||
val quickChatActions: StateFlow<List<QuickChatAction>> = _quickChatActions
|
val quickChatActions: StateFlow<List<QuickChatAction>> = _quickChatActions
|
||||||
|
|
@ -139,80 +142,10 @@ class UIViewModel @Inject constructor(
|
||||||
_requestChannelUrl.value = null
|
_requestChannelUrl.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
var positionBroadcastSecs: Int?
|
var region: Config.LoRaConfig.RegionCode
|
||||||
get() {
|
get() = config.lora?.region ?: Config.LoRaConfig.RegionCode.Unset
|
||||||
_localConfig.value?.position?.positionBroadcastSecs?.let {
|
|
||||||
return if (it > 0) it else defaultPositionBroadcastSecs
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
set(value) {
|
set(value) {
|
||||||
val config = _localConfig.value
|
updateLoraConfig { it.copy { region = value } }
|
||||||
if (value != null && config != null) {
|
|
||||||
val builder = config.position.toBuilder()
|
|
||||||
builder.positionBroadcastSecs =
|
|
||||||
if (value == defaultPositionBroadcastSecs) 0 else value
|
|
||||||
val newConfig = ConfigProtos.Config.newBuilder()
|
|
||||||
newConfig.position = builder.build()
|
|
||||||
setDeviceConfig(newConfig.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lsSleepSecs: Int?
|
|
||||||
get() {
|
|
||||||
_localConfig.value?.power?.lsSecs?.let {
|
|
||||||
return if (it > 0) it else defaultLsSecs
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
val config = _localConfig.value
|
|
||||||
if (value != null && config != null) {
|
|
||||||
val builder = config.power.toBuilder()
|
|
||||||
builder.lsSecs = if (value == defaultLsSecs) 0 else value
|
|
||||||
val newConfig = ConfigProtos.Config.newBuilder()
|
|
||||||
newConfig.power = builder.build()
|
|
||||||
setDeviceConfig(newConfig.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var gpsDisabled: Boolean
|
|
||||||
get() = _localConfig.value?.position?.gpsDisabled ?: false
|
|
||||||
set(value) {
|
|
||||||
val config = _localConfig.value
|
|
||||||
if (config != null) {
|
|
||||||
val builder = config.position.toBuilder()
|
|
||||||
builder.gpsDisabled = value
|
|
||||||
val newConfig = ConfigProtos.Config.newBuilder()
|
|
||||||
newConfig.position = builder.build()
|
|
||||||
setDeviceConfig(newConfig.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isPowerSaving: Boolean?
|
|
||||||
get() = _localConfig.value?.power?.isPowerSaving
|
|
||||||
set(value) {
|
|
||||||
val config = _localConfig.value
|
|
||||||
if (value != null && config != null) {
|
|
||||||
val builder = config.power.toBuilder()
|
|
||||||
builder.isPowerSaving = value
|
|
||||||
val newConfig = ConfigProtos.Config.newBuilder()
|
|
||||||
newConfig.power = builder.build()
|
|
||||||
setDeviceConfig(newConfig.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var region: ConfigProtos.Config.LoRaConfig.RegionCode
|
|
||||||
get() = localConfig.value?.lora?.region ?: ConfigProtos.Config.LoRaConfig.RegionCode.Unset
|
|
||||||
set(value) {
|
|
||||||
val config = _localConfig.value
|
|
||||||
if (config != null) {
|
|
||||||
val builder = config.lora.toBuilder()
|
|
||||||
builder.region = value
|
|
||||||
val newConfig = ConfigProtos.Config.newBuilder()
|
|
||||||
newConfig.lora = builder.build()
|
|
||||||
setDeviceConfig(newConfig.build())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun gpsString(pos: Position): String {
|
fun gpsString(pos: Position): String {
|
||||||
|
|
@ -226,12 +159,7 @@ class UIViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
val isRouter: Boolean =
|
val isRouter: Boolean = config.device?.role == Config.DeviceConfig.Role.Router
|
||||||
localConfig.value?.device?.role == ConfigProtos.Config.DeviceConfig.Role.Router
|
|
||||||
|
|
||||||
// These default values are borrowed from the device code.
|
|
||||||
private val defaultPositionBroadcastSecs = if (isRouter) 12 * 60 * 60 else 15 * 60
|
|
||||||
private val defaultLsSecs = if (isRouter) 24 * 60 * 60 else 5 * 60
|
|
||||||
|
|
||||||
// We consider hasWifi = ESP32
|
// We consider hasWifi = ESP32
|
||||||
fun isESP32() = myNodeInfo.value?.hasWifi == true
|
fun isESP32() = myNodeInfo.value?.hasWifi == true
|
||||||
|
|
@ -272,19 +200,44 @@ class UIViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
inline fun updateDeviceConfig(crossinline body: (Config.DeviceConfig) -> Config.DeviceConfig) {
|
||||||
* Return the primary channel info
|
val data = body(config.device)
|
||||||
*/
|
setDeviceConfig(config { device = data })
|
||||||
val primaryChannel: Channel? get() = _channels.value?.primaryChannel
|
|
||||||
|
|
||||||
// Set the radio config (also updates our saved copy in preferences)
|
|
||||||
private fun setDeviceConfig(config: ConfigProtos.Config) {
|
|
||||||
meshService?.deviceConfig = config.toByteArray()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLocalConfig(localConfig: LocalOnlyProtos.LocalConfig) {
|
inline fun updatePositionConfig(crossinline body: (Config.PositionConfig) -> Config.PositionConfig) {
|
||||||
if (_localConfig.value == localConfig) return
|
val data = body(config.position)
|
||||||
_localConfig.value = localConfig
|
setDeviceConfig(config { position = data })
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun updatePowerConfig(crossinline body: (Config.PowerConfig) -> Config.PowerConfig) {
|
||||||
|
val data = body(config.power)
|
||||||
|
setDeviceConfig(config { power = data })
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun updateNetworkConfig(crossinline body: (Config.WiFiConfig) -> Config.WiFiConfig) {
|
||||||
|
val data = body(config.wifi)
|
||||||
|
setDeviceConfig(config { wifi = data })
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun updateDisplayConfig(crossinline body: (Config.DisplayConfig) -> Config.DisplayConfig) {
|
||||||
|
val data = body(config.display)
|
||||||
|
setDeviceConfig(config { display = data })
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun updateLoraConfig(crossinline body: (Config.LoRaConfig) -> Config.LoRaConfig) {
|
||||||
|
val data = body(config.lora)
|
||||||
|
setDeviceConfig(config { lora = data })
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun updateBluetoothConfig(crossinline body: (Config.BluetoothConfig) -> Config.BluetoothConfig) {
|
||||||
|
val data = body(config.bluetooth)
|
||||||
|
setDeviceConfig(config { bluetooth = data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the radio config (also updates our saved copy in preferences)
|
||||||
|
fun setDeviceConfig(config: Config) {
|
||||||
|
meshService?.deviceConfig = config.toByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the radio config (also updates our saved copy in preferences)
|
/// Set the radio config (also updates our saved copy in preferences)
|
||||||
|
|
|
||||||
|
|
@ -493,10 +493,8 @@ class MeshService : Service(), Logging {
|
||||||
setChannel(it)
|
setChannel(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val newConfig = ConfigProtos.Config.newBuilder()
|
val newConfig = config { lora = value.loraConfig }
|
||||||
val newPrefs = (value.loraConfig).toBuilder()
|
if (localConfig.lora != newConfig.lora) sendDeviceConfig(newConfig)
|
||||||
newConfig.lora = newPrefs.build()
|
|
||||||
if (localConfig.lora != newConfig.lora) sendDeviceConfig(newConfig.build())
|
|
||||||
|
|
||||||
channels = fixupChannelList(asChannels)
|
channels = fixupChannelList(asChannels)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import com.geeksville.mesh.android.Logging
|
import com.geeksville.mesh.android.Logging
|
||||||
import com.geeksville.mesh.android.hideKeyboard
|
import com.geeksville.mesh.android.hideKeyboard
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
|
import com.geeksville.mesh.copy
|
||||||
import com.geeksville.mesh.databinding.AdvancedSettingsBinding
|
import com.geeksville.mesh.databinding.AdvancedSettingsBinding
|
||||||
import com.geeksville.mesh.model.ChannelOption
|
import com.geeksville.mesh.model.ChannelOption
|
||||||
import com.geeksville.mesh.model.UIViewModel
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
|
|
@ -38,19 +40,19 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
model.localConfig.observe(viewLifecycleOwner) {
|
model.localConfig.asLiveData().observe(viewLifecycleOwner) {
|
||||||
binding.positionBroadcastPeriodEditText.setText(model.positionBroadcastSecs.toString())
|
binding.positionBroadcastPeriodEditText.setText(model.config.position.positionBroadcastSecs.toString())
|
||||||
binding.lsSleepEditText.setText(model.lsSleepSecs.toString())
|
binding.lsSleepEditText.setText(model.config.power.lsSecs.toString())
|
||||||
binding.positionBroadcastPeriodView.isEnabled = !model.gpsDisabled
|
binding.positionBroadcastPeriodView.isEnabled = !model.config.position.gpsDisabled
|
||||||
binding.positionBroadcastSwitch.isChecked = !model.gpsDisabled
|
binding.positionBroadcastSwitch.isChecked = !model.config.position.gpsDisabled
|
||||||
binding.lsSleepView.isEnabled = model.isPowerSaving ?: false && model.isESP32()
|
binding.lsSleepView.isEnabled = model.config.power.isPowerSaving && model.isESP32()
|
||||||
binding.lsSleepSwitch.isChecked = model.isPowerSaving ?: false && model.isESP32()
|
binding.lsSleepSwitch.isChecked = model.config.power.isPowerSaving && model.isESP32()
|
||||||
}
|
}
|
||||||
|
|
||||||
model.connectionState.observe(viewLifecycleOwner) { connectionState ->
|
model.connectionState.observe(viewLifecycleOwner) { connectionState ->
|
||||||
val connected = connectionState == MeshService.ConnectionState.CONNECTED
|
val connected = connectionState == MeshService.ConnectionState.CONNECTED
|
||||||
binding.positionBroadcastPeriodView.isEnabled = connected && !model.gpsDisabled
|
binding.positionBroadcastPeriodView.isEnabled = connected && !model.config.position.gpsDisabled
|
||||||
binding.lsSleepView.isEnabled = connected && model.isPowerSaving ?: false
|
binding.lsSleepView.isEnabled = connected && model.config.power.isPowerSaving
|
||||||
binding.positionBroadcastSwitch.isEnabled = connected
|
binding.positionBroadcastSwitch.isEnabled = connected
|
||||||
binding.lsSleepSwitch.isEnabled = connected && model.isESP32()
|
binding.lsSleepSwitch.isEnabled = connected && model.isESP32()
|
||||||
binding.shutdownButton.isEnabled = connected && model.hasAXP()
|
binding.shutdownButton.isEnabled = connected && model.hasAXP()
|
||||||
|
|
@ -62,16 +64,16 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
||||||
val textEdit = binding.positionBroadcastPeriodEditText
|
val textEdit = binding.positionBroadcastPeriodEditText
|
||||||
val n = textEdit.text.toString().toIntOrNull()
|
val n = textEdit.text.toString().toIntOrNull()
|
||||||
val minBroadcastPeriodSecs =
|
val minBroadcastPeriodSecs =
|
||||||
ChannelOption.fromConfig(model.localConfig.value?.lora?.modemPreset)?.minBroadcastPeriodSecs
|
ChannelOption.fromConfig(model.config.lora.modemPreset)?.minBroadcastPeriodSecs
|
||||||
?: ChannelOption.defaultMinBroadcastPeriod
|
?: ChannelOption.defaultMinBroadcastPeriod
|
||||||
|
|
||||||
if (n != null && n < MAX_INT_DEVICE && (n == 0 || n >= minBroadcastPeriodSecs)) {
|
if (n != null && n < MAX_INT_DEVICE && (n == 0 || n >= minBroadcastPeriodSecs)) {
|
||||||
exceptionToSnackbar(requireView()) {
|
exceptionToSnackbar(requireView()) {
|
||||||
model.positionBroadcastSecs = n
|
model.updatePositionConfig { it.copy { positionBroadcastSecs = n } }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// restore the value in the edit field
|
// restore the value in the edit field
|
||||||
textEdit.setText(model.positionBroadcastSecs.toString())
|
textEdit.setText(model.config.position.positionBroadcastSecs.toString())
|
||||||
val errorText =
|
val errorText =
|
||||||
if (n == null || n < 0 || n >= MAX_INT_DEVICE)
|
if (n == null || n < 0 || n >= MAX_INT_DEVICE)
|
||||||
"Bad value: ${textEdit.text.toString()}"
|
"Bad value: ${textEdit.text.toString()}"
|
||||||
|
|
@ -83,9 +85,9 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
||||||
requireActivity().hideKeyboard()
|
requireActivity().hideKeyboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.positionBroadcastSwitch.setOnCheckedChangeListener { view, isChecked ->
|
binding.positionBroadcastSwitch.setOnCheckedChangeListener { btn, isChecked ->
|
||||||
if (view.isPressed) {
|
if (btn.isPressed) {
|
||||||
model.gpsDisabled = !isChecked
|
model.updatePositionConfig { it.copy { gpsDisabled = !isChecked } }
|
||||||
debug("User changed locationShare to $isChecked")
|
debug("User changed locationShare to $isChecked")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +97,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
||||||
val n = str.toIntOrNull()
|
val n = str.toIntOrNull()
|
||||||
if (n != null && n < MAX_INT_DEVICE && n >= 0) {
|
if (n != null && n < MAX_INT_DEVICE && n >= 0) {
|
||||||
exceptionToSnackbar(requireView()) {
|
exceptionToSnackbar(requireView()) {
|
||||||
model.lsSleepSecs = n
|
model.updatePowerConfig { it.copy { lsSecs = n } }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Snackbar.make(requireView(), "Bad value: $str", Snackbar.LENGTH_LONG).show()
|
Snackbar.make(requireView(), "Bad value: $str", Snackbar.LENGTH_LONG).show()
|
||||||
|
|
@ -103,9 +105,9 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
|
||||||
requireActivity().hideKeyboard()
|
requireActivity().hideKeyboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.lsSleepSwitch.setOnCheckedChangeListener { view, isChecked ->
|
binding.lsSleepSwitch.setOnCheckedChangeListener { btn, isChecked ->
|
||||||
if (view.isPressed) {
|
if (btn.isPressed) {
|
||||||
model.isPowerSaving = isChecked
|
model.updatePowerConfig { it.copy { isPowerSaving = isChecked } }
|
||||||
debug("User changed isPowerSaving to $isChecked")
|
debug("User changed isPowerSaving to $isChecked")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import android.widget.*
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.flowWithLifecycle
|
import androidx.lifecycle.flowWithLifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.geeksville.mesh.analytics.DataPair
|
import com.geeksville.mesh.analytics.DataPair
|
||||||
|
|
@ -160,9 +161,9 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
if (connected == MeshService.ConnectionState.DISCONNECTED)
|
if (connected == MeshService.ConnectionState.DISCONNECTED)
|
||||||
model.setOwner("")
|
model.setOwner("")
|
||||||
|
|
||||||
if (model.gpsDisabled) {
|
if (model.config.position.gpsDisabled) {
|
||||||
model.provideLocation.value = false
|
|
||||||
binding.provideLocationCheckbox.isChecked = false
|
binding.provideLocationCheckbox.isChecked = false
|
||||||
|
binding.provideLocationCheckbox.isEnabled = false
|
||||||
} else {
|
} else {
|
||||||
binding.provideLocationCheckbox.isEnabled = true
|
binding.provideLocationCheckbox.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +288,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
||||||
updateDevicesButtons(scanModel.devices.value)
|
updateDevicesButtons(scanModel.devices.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
model.localConfig.observe(viewLifecycleOwner) {
|
model.localConfig.asLiveData().observe(viewLifecycleOwner) {
|
||||||
if (!model.isConnected()) {
|
if (!model.isConnected()) {
|
||||||
val configCount = it.allFields.size
|
val configCount = it.allFields.size
|
||||||
binding.scanStatusText.text = "Device config ($configCount / 7)"
|
binding.scanStatusText.text = "Device config ($configCount / 7)"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue