From 8bde3d2ba4423a4600c81e73ae3fe97ee1ec7c7b Mon Sep 17 00:00:00 2001 From: andrekir Date: Fri, 22 Apr 2022 17:22:06 -0300 Subject: [PATCH] make MutableLiveData private --- .../java/com/geeksville/mesh/MainActivity.kt | 55 ++++------- .../java/com/geeksville/mesh/model/NodeDB.kt | 21 +++-- .../java/com/geeksville/mesh/model/UIState.kt | 91 ++++++++++++------- .../geeksville/mesh/ui/SettingsFragment.kt | 2 +- 4 files changed, 92 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index cb13edb5b..3373ee423 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -417,7 +417,7 @@ class MainActivity : BaseActivity(), Logging, binding = ActivityMainBinding.inflate(layoutInflater) val prefs = UIViewModel.getPreferences(this) - model.ownerName.value = prefs.getString("owner", "")!! + model.setOwner(prefs.getString("owner", "")) /// Set theme setUITheme(prefs) @@ -605,24 +605,6 @@ class MainActivity : BaseActivity(), Logging, } } - /// Pull our latest node db from the device - private fun updateNodesFromDevice() { - model.meshService?.let { service -> - // Update our nodeinfos based on data from the device - val nodes = service.nodes.associateBy { it.user?.id!! } - model.nodeDB.nodes.value = nodes - - try { - // Pull down our real node ID - This must be done AFTER reading the nodedb because we need the DB to find our nodeinof object - model.nodeDB.myId.value = service.myId - val ourNodeInfo = model.nodeDB.ourNodeInfo - model.ownerName.value = ourNodeInfo?.user?.longName - } catch (ex: Exception) { - warn("Ignoring failure to get myId, service is probably just uninited... ${ex.message}") - } - } - } - /** Show an alert that may contain HTML */ private fun showAlert(titleText: Int, messageText: Int) { // make links clickable per https://stackoverflow.com/a/62642807 @@ -646,19 +628,19 @@ class MainActivity : BaseActivity(), Logging, } /// Called when we gain/lose a connection to our mesh radio - private fun onMeshConnectionChanged(connected: MeshService.ConnectionState) { - debug("connchange ${model.isConnected.value} -> $connected") + private fun onMeshConnectionChanged(newConnection: MeshService.ConnectionState) { + val oldConnection = model.isConnected.value!! + debug("connchange $oldConnection -> $newConnection") - if (connected == MeshService.ConnectionState.CONNECTED) { + if (newConnection == MeshService.ConnectionState.CONNECTED) { model.meshService?.let { service -> - val oldConnection = model.isConnected.value - model.isConnected.value = connected + model.setConnectionState(newConnection) debug("Getting latest radioconfig from service") try { val info: MyNodeInfo? = service.myNodeInfo // this can be null - model.myNodeInfo.value = info + model.setMyNodeInfo(info) if (info != null) { val isOld = info.minAppVersion > BuildConfig.VERSION_CODE @@ -675,13 +657,11 @@ class MainActivity : BaseActivity(), Logging, 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 - model.radioConfig.value = - RadioConfigProtos.RadioConfig.parseFrom(service.radioConfig) + model.setRadioConfig(RadioConfigProtos.RadioConfig.parseFrom(service.radioConfig)) - model.channels.value = - ChannelSet(AppOnlyProtos.ChannelSet.parseFrom(service.channels)) + model.setChannels(ChannelSet(AppOnlyProtos.ChannelSet.parseFrom(service.channels))) - updateNodesFromDevice() + model.updateNodesFromDevice() // we have a connection to our device now, do the channel change perhapsChangeChannel() @@ -691,7 +671,7 @@ class MainActivity : BaseActivity(), Logging, } } catch (ex: RemoteException) { warn("Abandoning connect $ex, because we probably just lost device connection") - model.isConnected.value = oldConnection + model.setConnectionState(oldConnection) } // if provideLocation enabled: Start providing location (from phone GPS) to mesh if (model.provideLocation.value == true) @@ -699,7 +679,7 @@ class MainActivity : BaseActivity(), Logging, } } else { // For other connection states, just slam them in - model.isConnected.value = connected + model.setConnectionState(newConnection) } } @@ -793,8 +773,8 @@ class MainActivity : BaseActivity(), Logging, // We only care about nodes that have user info info.user?.id?.let { - val newnodes = model.nodeDB.nodes.value!! + Pair(it, info) - model.nodeDB.nodes.value = newnodes + val nodes = model.nodeDB.nodes.value!! + Pair(it, info) + model.nodeDB.setNodes(nodes) } } @@ -887,7 +867,7 @@ class MainActivity : BaseActivity(), Logging, val msgs = allMsgs.filter { p -> p.dataType == Portnums.PortNum.TEXT_MESSAGE_APP_VALUE } - model.myNodeInfo.value = service.myNodeInfo // Note: this could be NULL! + model.setMyNodeInfo(service.myNodeInfo) // Note: this could be NULL! debug("Service provided ${msgs.size} messages and myNodeNum ${model.myNodeInfo.value?.myNodeNum}") model.messagesState.setMessages(msgs) @@ -897,15 +877,14 @@ class MainActivity : BaseActivity(), Logging, // if we are not connected, onMeshConnectionChange won't fetch nodes from the service // in that case, we do it here - because the service certainly has a better idea of node db that we have if (connectionState != MeshService.ConnectionState.CONNECTED) - updateNodesFromDevice() + model.updateNodesFromDevice() // We won't receive a notify for the initial state of connection, so we force an update here onMeshConnectionChanged(connectionState) } catch (ex: RemoteException) { // If we get an exception while reading our service config, the device might have gone away, double check to see if we are really connected errormsg("Device error during init ${ex.message}") - model.isConnected.value = - MeshService.ConnectionState.valueOf(service.connectionState()) + model.setConnectionState(MeshService.ConnectionState.valueOf(service.connectionState())) } finally { connectionJob = null } diff --git a/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt b/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt index fee2dfa67..877e35a75 100644 --- a/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt +++ b/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt @@ -1,5 +1,6 @@ package com.geeksville.mesh.model +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.geeksville.mesh.MeshProtos import com.geeksville.mesh.MeshUser @@ -26,7 +27,7 @@ class NodeDB(private val ui: UIViewModel) { null ) - val testNodes = testPositions.mapIndexed { index, it -> + private val testNodes = testPositions.mapIndexed { index, it -> NodeInfo( 9 + index, MeshUser( @@ -42,13 +43,21 @@ class NodeDB(private val ui: UIViewModel) { private val seedWithTestNodes = false /// The unique ID of our node - val myId = object : MutableLiveData(if (seedWithTestNodes) "+16508765309" else null) {} + private val _myId = MutableLiveData(if (seedWithTestNodes) "+16508765309" else null) + val myId: LiveData get() = _myId + + fun setMyId(myId: String?) { + _myId.value = myId + } /// A map from nodeid to to nodeinfo - val nodes = - object : - MutableLiveData>(mapOf(*(if (seedWithTestNodes) testNodes else listOf()).map { it.user!!.id to it } - .toTypedArray())) {} + private val _nodes = MutableLiveData>(mapOf(*(if (seedWithTestNodes) testNodes else listOf()).map { it.user!!.id to it } + .toTypedArray())) + val nodes: LiveData> get() = _nodes + + fun setNodes(nodes: Map) { + _nodes.value = nodes + } /// Could be null if we haven't received our node DB yet val ourNodeInfo get() = nodes.value?.get(myId.value) 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 eb2cea96d..38f487fd7 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -7,6 +7,7 @@ import android.net.Uri import android.os.RemoteException import android.view.Menu import androidx.core.content.edit +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -89,22 +90,26 @@ class UIViewModel @Inject constructor( val nodeDB = NodeDB(this) val messagesState = MessagesState(this) - /// Are we connected to our radio device - val isConnected = - object : - MutableLiveData(MeshService.ConnectionState.DISCONNECTED) { - } + /// Connection state to our radio device + private val _connectionState = MutableLiveData(MeshService.ConnectionState.DISCONNECTED) + val isConnected: LiveData get() = _connectionState + + // fun isConnected() = _connectionState.value == MeshService.ConnectionState.CONNECTED + + fun setConnectionState(connectionState: MeshService.ConnectionState) { + _connectionState.value = connectionState + } /// various radio settings (including the channel) - val radioConfig = object : MutableLiveData(null) { - } + private val _radioConfig = MutableLiveData() + val radioConfig: LiveData get() = _radioConfig - val channels = object : MutableLiveData(null) { - } + private val _channels = MutableLiveData() + val channels: LiveData get() = _channels var positionBroadcastSecs: Int? get() { - radioConfig.value?.preferences?.let { + _radioConfig.value?.preferences?.let { if (it.positionBroadcastSecs > 0) return it.positionBroadcastSecs // These default values are borrowed from the device code. return 15 * 60 @@ -112,7 +117,7 @@ class UIViewModel @Inject constructor( return null } set(value) { - val config = radioConfig.value + val config = _radioConfig.value if (value != null && config != null) { val builder = config.toBuilder() builder.preferencesBuilder.positionBroadcastSecs = value @@ -121,9 +126,9 @@ class UIViewModel @Inject constructor( } var lsSleepSecs: Int? - get() = radioConfig.value?.preferences?.lsSecs + get() = _radioConfig.value?.preferences?.lsSecs set(value) { - val config = radioConfig.value + val config = _radioConfig.value if (value != null && config != null) { val builder = config.toBuilder() builder.preferencesBuilder.lsSecs = value @@ -132,9 +137,9 @@ class UIViewModel @Inject constructor( } var locationShareDisabled: Boolean - get() = radioConfig.value?.preferences?.locationShareDisabled ?: false + get() = _radioConfig.value?.preferences?.locationShareDisabled ?: false set(value) { - val config = radioConfig.value + val config = _radioConfig.value if (config != null) { val builder = config.toBuilder() builder.preferencesBuilder.locationShareDisabled = value @@ -143,9 +148,9 @@ class UIViewModel @Inject constructor( } var isPowerSaving: Boolean? - get() = radioConfig.value?.preferences?.isPowerSaving + get() = _radioConfig.value?.preferences?.isPowerSaving set(value) { - val config = radioConfig.value + val config = _radioConfig.value if (value != null && config != null) { val builder = config.toBuilder() builder.preferencesBuilder.isPowerSaving = value @@ -161,24 +166,46 @@ class UIViewModel @Inject constructor( } /// hardware info about our local device (can be null) - val myNodeInfo = object : MutableLiveData(null) {} + private val _myNodeInfo = MutableLiveData() + val myNodeInfo: LiveData get() = _myNodeInfo + + fun setMyNodeInfo(info: MyNodeInfo?) { + _myNodeInfo.value = info + } override fun onCleared() { super.onCleared() debug("ViewModel cleared") } + /// Pull our latest node db from the device + fun updateNodesFromDevice() { + meshService?.let { service -> + // Update our nodeinfos based on data from the device + val nodes = service.nodes.associateBy { it.user?.id!! } + nodeDB.setNodes(nodes) + + try { + // Pull down our real node ID - This must be done AFTER reading the nodedb because we need the DB to find our nodeinof object + nodeDB.setMyId(service.myId) + val ownerName = nodeDB.ourNodeInfo?.user?.longName + _ownerName.value = ownerName + } catch (ex: Exception) { + warn("Ignoring failure to get myId, service is probably just uninited... ${ex.message}") + } + } + } + /** * Return the primary channel info */ - val primaryChannel: Channel? get() = channels.value?.primaryChannel + val primaryChannel: Channel? get() = _channels.value?.primaryChannel - /// // Set the radio config (also updates our saved copy in preferences) - private fun setRadioConfig(c: RadioConfigProtos.RadioConfig) { + fun setRadioConfig(c: RadioConfigProtos.RadioConfig) { debug("Setting new radio config!") meshService?.radioConfig = c.toByteArray() - radioConfig.value = + _radioConfig.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) } @@ -186,10 +213,10 @@ class UIViewModel @Inject constructor( fun setChannels(c: ChannelSet) { debug("Setting new channels!") meshService?.channels = c.protobuf.toByteArray() - channels.value = + _channels.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) - preferences.edit(commit = true) { + preferences.edit { this.putString("channel-url", c.getChannelUrl().toString()) } } @@ -200,14 +227,14 @@ class UIViewModel @Inject constructor( /// our name in hte radio /// Note, we generate owner initials automatically for now /// our activity will read this from prefs or set it to the empty string - val ownerName = object : MutableLiveData("MrIDE Test") { - } + private val _ownerName = MutableLiveData() + val ownerName: LiveData get() = _ownerName val provideLocation = object : MutableLiveData(preferences.getBoolean(MyPreferences.provideLocationKey, false)) { override fun setValue(value: Boolean) { super.setValue(value) - preferences.edit(commit = true) { + preferences.edit { this.putBoolean(MyPreferences.provideLocationKey, value) } } @@ -217,21 +244,21 @@ class UIViewModel @Inject constructor( fun setOwner(s: String? = null) { if (s != null) { - ownerName.value = s + _ownerName.value = s // note: we allow an empty userstring to be written to prefs - preferences.edit(commit = true) { + preferences.edit { putString("owner", s) } } // Note: we are careful to not set a new unique ID - if (ownerName.value!!.isNotEmpty()) + if (_ownerName.value!!.isNotEmpty()) try { meshService?.setOwner( null, - ownerName.value, - getInitials(ownerName.value!!) + _ownerName.value, + getInitials(_ownerName.value!!) ) // Note: we use ?. here because we might be running in the emulator } catch (ex: RemoteException) { errormsg("Can't set username on device, is device offline? ${ex.message}") 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 6f3199a76..9385f62ea 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -568,7 +568,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { binding.provideLocationCheckbox.visibility = if (isConnected) View.VISIBLE else View.GONE if (connected == MeshService.ConnectionState.DISCONNECTED) - model.ownerName.value = "" + model.setOwner("") // update the region selection from the device val region = model.region