From fd0c8ef9b81a670f3151ace2fb3be8e47bb673d9 Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 12 Sep 2022 00:26:12 -0300 Subject: [PATCH] refactor config get and set methods --- .../java/com/geeksville/mesh/model/UIState.kt | 137 ++++++------------ .../geeksville/mesh/service/MeshService.kt | 6 +- .../mesh/ui/AdvancedSettingsFragment.kt | 40 ++--- .../geeksville/mesh/ui/SettingsFragment.kt | 7 +- 4 files changed, 72 insertions(+), 118 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index 5b9d8b6a..df50109e 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -13,10 +13,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.geeksville.mesh.android.Logging import com.geeksville.mesh.* +import com.geeksville.mesh.ConfigProtos.Config import com.geeksville.mesh.database.PacketRepository import com.geeksville.mesh.database.QuickChatActionRepository import com.geeksville.mesh.database.entity.Packet import com.geeksville.mesh.database.entity.QuickChatAction +import com.geeksville.mesh.LocalOnlyProtos.LocalConfig import com.geeksville.mesh.repository.datastore.LocalConfigRepository import com.geeksville.mesh.service.MeshService import com.geeksville.mesh.util.GPSFormat @@ -71,8 +73,9 @@ class UIViewModel @Inject constructor( private val _allPacketState = MutableStateFlow>(emptyList()) val allPackets: StateFlow> = _allPacketState - private val _localConfig = MutableLiveData() - val localConfig: LiveData get() = _localConfig + private val _localConfig = MutableStateFlow(LocalConfig.getDefaultInstance()) + val localConfig: StateFlow = _localConfig + val config get() = _localConfig.value private val _quickChatActions = MutableStateFlow>(emptyList()) val quickChatActions: StateFlow> = _quickChatActions @@ -139,80 +142,10 @@ class UIViewModel @Inject constructor( _requestChannelUrl.value = null } - var positionBroadcastSecs: Int? - get() { - _localConfig.value?.position?.positionBroadcastSecs?.let { - return if (it > 0) it else defaultPositionBroadcastSecs - } - return null - } + var region: Config.LoRaConfig.RegionCode + get() = config.lora?.region ?: Config.LoRaConfig.RegionCode.Unset set(value) { - val config = _localConfig.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()) - } + updateLoraConfig { it.copy { region = value } } } fun gpsString(pos: Position): String { @@ -226,12 +159,7 @@ class UIViewModel @Inject constructor( } @Suppress("MemberVisibilityCanBePrivate") - val isRouter: Boolean = - 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 + val isRouter: Boolean = config.device?.role == Config.DeviceConfig.Role.Router // We consider hasWifi = ESP32 fun isESP32() = myNodeInfo.value?.hasWifi == true @@ -272,19 +200,44 @@ class UIViewModel @Inject constructor( } } - /** - * Return the primary channel info - */ - 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() + inline fun updateDeviceConfig(crossinline body: (Config.DeviceConfig) -> Config.DeviceConfig) { + val data = body(config.device) + setDeviceConfig(config { device = data }) } - fun setLocalConfig(localConfig: LocalOnlyProtos.LocalConfig) { - if (_localConfig.value == localConfig) return - _localConfig.value = localConfig + inline fun updatePositionConfig(crossinline body: (Config.PositionConfig) -> Config.PositionConfig) { + val data = body(config.position) + 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) diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 57055828..b1de36a7 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -493,10 +493,8 @@ class MeshService : Service(), Logging { setChannel(it) } - val newConfig = ConfigProtos.Config.newBuilder() - val newPrefs = (value.loraConfig).toBuilder() - newConfig.lora = newPrefs.build() - if (localConfig.lora != newConfig.lora) sendDeviceConfig(newConfig.build()) + val newConfig = config { lora = value.loraConfig } + if (localConfig.lora != newConfig.lora) sendDeviceConfig(newConfig) channels = fixupChannelList(asChannels) } diff --git a/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt index 1539459a..ecf599e2 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt @@ -6,9 +6,11 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import androidx.fragment.app.activityViewModels +import androidx.lifecycle.asLiveData import com.geeksville.mesh.android.Logging import com.geeksville.mesh.android.hideKeyboard import com.geeksville.mesh.R +import com.geeksville.mesh.copy import com.geeksville.mesh.databinding.AdvancedSettingsBinding import com.geeksville.mesh.model.ChannelOption import com.geeksville.mesh.model.UIViewModel @@ -38,19 +40,19 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - model.localConfig.observe(viewLifecycleOwner) { - binding.positionBroadcastPeriodEditText.setText(model.positionBroadcastSecs.toString()) - binding.lsSleepEditText.setText(model.lsSleepSecs.toString()) - binding.positionBroadcastPeriodView.isEnabled = !model.gpsDisabled - binding.positionBroadcastSwitch.isChecked = !model.gpsDisabled - binding.lsSleepView.isEnabled = model.isPowerSaving ?: false && model.isESP32() - binding.lsSleepSwitch.isChecked = model.isPowerSaving ?: false && model.isESP32() + model.localConfig.asLiveData().observe(viewLifecycleOwner) { + binding.positionBroadcastPeriodEditText.setText(model.config.position.positionBroadcastSecs.toString()) + binding.lsSleepEditText.setText(model.config.power.lsSecs.toString()) + binding.positionBroadcastPeriodView.isEnabled = !model.config.position.gpsDisabled + binding.positionBroadcastSwitch.isChecked = !model.config.position.gpsDisabled + binding.lsSleepView.isEnabled = model.config.power.isPowerSaving && model.isESP32() + binding.lsSleepSwitch.isChecked = model.config.power.isPowerSaving && model.isESP32() } model.connectionState.observe(viewLifecycleOwner) { connectionState -> val connected = connectionState == MeshService.ConnectionState.CONNECTED - binding.positionBroadcastPeriodView.isEnabled = connected && !model.gpsDisabled - binding.lsSleepView.isEnabled = connected && model.isPowerSaving ?: false + binding.positionBroadcastPeriodView.isEnabled = connected && !model.config.position.gpsDisabled + binding.lsSleepView.isEnabled = connected && model.config.power.isPowerSaving binding.positionBroadcastSwitch.isEnabled = connected binding.lsSleepSwitch.isEnabled = connected && model.isESP32() binding.shutdownButton.isEnabled = connected && model.hasAXP() @@ -62,16 +64,16 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { val textEdit = binding.positionBroadcastPeriodEditText val n = textEdit.text.toString().toIntOrNull() val minBroadcastPeriodSecs = - ChannelOption.fromConfig(model.localConfig.value?.lora?.modemPreset)?.minBroadcastPeriodSecs + ChannelOption.fromConfig(model.config.lora.modemPreset)?.minBroadcastPeriodSecs ?: ChannelOption.defaultMinBroadcastPeriod if (n != null && n < MAX_INT_DEVICE && (n == 0 || n >= minBroadcastPeriodSecs)) { exceptionToSnackbar(requireView()) { - model.positionBroadcastSecs = n + model.updatePositionConfig { it.copy { positionBroadcastSecs = n } } } } else { // restore the value in the edit field - textEdit.setText(model.positionBroadcastSecs.toString()) + textEdit.setText(model.config.position.positionBroadcastSecs.toString()) val errorText = if (n == null || n < 0 || n >= MAX_INT_DEVICE) "Bad value: ${textEdit.text.toString()}" @@ -83,9 +85,9 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { requireActivity().hideKeyboard() } - binding.positionBroadcastSwitch.setOnCheckedChangeListener { view, isChecked -> - if (view.isPressed) { - model.gpsDisabled = !isChecked + binding.positionBroadcastSwitch.setOnCheckedChangeListener { btn, isChecked -> + if (btn.isPressed) { + model.updatePositionConfig { it.copy { gpsDisabled = !isChecked } } debug("User changed locationShare to $isChecked") } } @@ -95,7 +97,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { val n = str.toIntOrNull() if (n != null && n < MAX_INT_DEVICE && n >= 0) { exceptionToSnackbar(requireView()) { - model.lsSleepSecs = n + model.updatePowerConfig { it.copy { lsSecs = n } } } } else { Snackbar.make(requireView(), "Bad value: $str", Snackbar.LENGTH_LONG).show() @@ -103,9 +105,9 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging { requireActivity().hideKeyboard() } - binding.lsSleepSwitch.setOnCheckedChangeListener { view, isChecked -> - if (view.isPressed) { - model.isPowerSaving = isChecked + binding.lsSleepSwitch.setOnCheckedChangeListener { btn, isChecked -> + if (btn.isPressed) { + model.updatePowerConfig { it.copy { isPowerSaving = isChecked } } debug("User changed isPowerSaving to $isChecked") } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index 36c4c0ff..59855e58 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -13,6 +13,7 @@ import android.widget.* import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle +import androidx.lifecycle.asLiveData import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.geeksville.mesh.analytics.DataPair @@ -160,9 +161,9 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { if (connected == MeshService.ConnectionState.DISCONNECTED) model.setOwner("") - if (model.gpsDisabled) { - model.provideLocation.value = false + if (model.config.position.gpsDisabled) { binding.provideLocationCheckbox.isChecked = false + binding.provideLocationCheckbox.isEnabled = false } else { binding.provideLocationCheckbox.isEnabled = true } @@ -287,7 +288,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { updateDevicesButtons(scanModel.devices.value) } - model.localConfig.observe(viewLifecycleOwner) { + model.localConfig.asLiveData().observe(viewLifecycleOwner) { if (!model.isConnected()) { val configCount = it.allFields.size binding.scanStatusText.text = "Device config ($configCount / 7)"