mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor: convert NodeDB to repository
This commit is contained in:
parent
d1d2c6cf3d
commit
c489717ad1
13 changed files with 110 additions and 101 deletions
|
|
@ -180,10 +180,6 @@ class MainActivity : AppCompatActivity(), Logging {
|
|||
override fun createFragment(position: Int): Fragment = tabInfos[position].content
|
||||
}
|
||||
|
||||
private val isInTestLab: Boolean by lazy {
|
||||
(application as GeeksvilleApplication).isInTestLab
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -364,7 +360,6 @@ class MainActivity : AppCompatActivity(), Logging {
|
|||
debug("Getting latest DeviceConfig from service")
|
||||
try {
|
||||
val info: MyNodeInfo? = service.myNodeInfo // this can be null
|
||||
model.setMyNodeInfo(info)
|
||||
|
||||
if (info != null) {
|
||||
val isOld = info.minAppVersion > BuildConfig.VERSION_CODE
|
||||
|
|
@ -381,9 +376,6 @@ class MainActivity : AppCompatActivity(), Logging {
|
|||
else {
|
||||
// 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.setLocalConfig(LocalOnlyProtos.LocalConfig.parseFrom(service.deviceConfig))
|
||||
// model.setChannels(ChannelSet(AppOnlyProtos.ChannelSet.parseFrom(service.channels)))
|
||||
|
||||
model.updateNodesFromDevice()
|
||||
|
||||
// we have a connection to our device now, do the channel change
|
||||
|
|
@ -526,8 +518,6 @@ class MainActivity : AppCompatActivity(), Logging {
|
|||
// We don't start listening for packets until after we are connected to the service
|
||||
registerMeshReceiver()
|
||||
|
||||
model.setMyNodeInfo(service.myNodeInfo) // Note: this could be NULL!
|
||||
|
||||
val connectionState =
|
||||
MeshService.ConnectionState.valueOf(service.connectionState())
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ interface MyNodeInfoDao {
|
|||
fun getMyNodeInfo(): Flow<MyNodeInfo?>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun setMyNodeInfo(myInfo: MyNodeInfo?)
|
||||
fun setMyNodeInfo(myInfo: MyNodeInfo)
|
||||
|
||||
@Query("DELETE FROM MyNodeInfo")
|
||||
fun clearMyNodeInfo()
|
||||
|
|
|
|||
|
|
@ -1,23 +1,50 @@
|
|||
package com.geeksville.mesh.model
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.asLiveData
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.MeshUser
|
||||
import com.geeksville.mesh.MyNodeInfo
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.Position
|
||||
import com.geeksville.mesh.database.dao.MyNodeInfoDao
|
||||
import com.geeksville.mesh.database.dao.NodeInfoDao
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NodeDB @Inject constructor(
|
||||
private val myNodeInfoDao: MyNodeInfoDao,
|
||||
private val nodeInfoDao: NodeInfoDao,
|
||||
) {
|
||||
|
||||
fun myNodeInfoFlow(): Flow<MyNodeInfo?> = myNodeInfoDao.getMyNodeInfo()
|
||||
private suspend fun setMyNodeInfo(myInfo: MyNodeInfo) = withContext(Dispatchers.IO) {
|
||||
myNodeInfoDao.setMyNodeInfo(myInfo)
|
||||
}
|
||||
|
||||
fun nodeInfoFlow(): Flow<List<NodeInfo>> = nodeInfoDao.getNodes()
|
||||
suspend fun upsert(node: NodeInfo) = withContext(Dispatchers.IO) {
|
||||
nodeInfoDao.upsert(node)
|
||||
}
|
||||
|
||||
suspend fun installNodeDB(mi: MyNodeInfo, nodes: List<NodeInfo>) {
|
||||
myNodeInfoDao.clearMyNodeInfo()
|
||||
nodeInfoDao.clearNodeInfo()
|
||||
nodeInfoDao.putAll(nodes)
|
||||
setMyNodeInfo(mi) // set MyNodeInfo last
|
||||
}
|
||||
|
||||
/// NodeDB lives inside the UIViewModel, but it needs a backpointer to reach the service
|
||||
class NodeDB(private val ui: UIViewModel) {
|
||||
private val testPositions = arrayOf(
|
||||
Position(32.776665, -96.796989, 35, 123), // dallas
|
||||
Position(32.960758, -96.733521, 35, 456), // richardson
|
||||
Position(32.912901, -96.781776, 35, 789) // north dallas
|
||||
Position(32.912901, -96.781776, 35, 789), // north dallas
|
||||
)
|
||||
|
||||
val testNodeNoPosition = NodeInfo(
|
||||
private val testNodeNoPosition = NodeInfo(
|
||||
8,
|
||||
MeshUser(
|
||||
"+16508765308".format(8),
|
||||
|
|
@ -29,7 +56,7 @@ class NodeDB(private val ui: UIViewModel) {
|
|||
null
|
||||
)
|
||||
|
||||
private val testNodes = testPositions.mapIndexed { index, it ->
|
||||
private val testNodes = (listOf(testNodeNoPosition) + testPositions.mapIndexed { index, it ->
|
||||
NodeInfo(
|
||||
9 + index,
|
||||
MeshUser(
|
||||
|
|
@ -41,22 +68,26 @@ class NodeDB(private val ui: UIViewModel) {
|
|||
),
|
||||
it
|
||||
)
|
||||
}.associateBy { it.user?.id!! }
|
||||
}).associateBy { it.user?.id!! }
|
||||
|
||||
private val seedWithTestNodes = false
|
||||
|
||||
// The unique userId of our node
|
||||
private val _myId = MutableLiveData<String?>(if (seedWithTestNodes) "+16508765309" else null)
|
||||
val myId: LiveData<String?> get() = _myId
|
||||
private val _myId = MutableStateFlow(if (seedWithTestNodes) "+16508765309" else null)
|
||||
val myId: StateFlow<String?> get() = _myId
|
||||
|
||||
fun setMyId(myId: String?) {
|
||||
_myId.value = myId
|
||||
}
|
||||
|
||||
// A map from nodeNum to NodeInfo
|
||||
private val _nodeDBbyNum = MutableStateFlow<Map<Int, NodeInfo>>(mapOf())
|
||||
val nodeDBbyNum: StateFlow<Map<Int, NodeInfo>> get() = _nodeDBbyNum
|
||||
val nodesByNum get() = nodeDBbyNum.value
|
||||
|
||||
// A map from userId to NodeInfo
|
||||
private val _nodes = MutableStateFlow(if (seedWithTestNodes) testNodes else mapOf())
|
||||
val nodes: LiveData<Map<String, NodeInfo>> = _nodes.asLiveData()
|
||||
val nodesByNum get() = nodes.value?.values?.associateBy { it.num }
|
||||
val nodes: StateFlow<Map<String, NodeInfo>> get() = _nodes
|
||||
|
||||
fun setNodes(nodes: Map<String, NodeInfo>) {
|
||||
_nodes.value = nodes
|
||||
|
|
@ -64,5 +95,6 @@ class NodeDB(private val ui: UIViewModel) {
|
|||
|
||||
fun setNodes(list: List<NodeInfo>) {
|
||||
setNodes(list.associateBy { it.user?.id!! })
|
||||
_nodeDBbyNum.value = list.associateBy { it.num }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import com.geeksville.mesh.database.entity.MeshLog
|
|||
import com.geeksville.mesh.deviceProfile
|
||||
import com.geeksville.mesh.moduleConfig
|
||||
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
|
||||
import com.geeksville.mesh.repository.radio.RadioInterfaceService
|
||||
import com.geeksville.mesh.service.MeshService.ConnectionState
|
||||
import com.geeksville.mesh.ui.ConfigRoute
|
||||
import com.geeksville.mesh.ui.ResponseState
|
||||
import com.google.protobuf.MessageLite
|
||||
|
|
@ -59,7 +57,6 @@ data class RadioConfigState(
|
|||
@HiltViewModel
|
||||
class RadioConfigViewModel @Inject constructor(
|
||||
private val app: Application,
|
||||
radioInterfaceService: RadioInterfaceService,
|
||||
private val radioConfigRepository: RadioConfigRepository,
|
||||
meshLogRepository: MeshLogRepository,
|
||||
) : ViewModel(), Logging {
|
||||
|
|
@ -68,12 +65,10 @@ class RadioConfigViewModel @Inject constructor(
|
|||
private val meshService: IMeshService? get() = radioConfigRepository.meshService
|
||||
|
||||
// Connection state to our radio device
|
||||
private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED)
|
||||
val connectionState: StateFlow<ConnectionState> get() = _connectionState
|
||||
val connectionState get() = radioConfigRepository.connectionState
|
||||
|
||||
// A map from nodeNum to NodeInfo
|
||||
private val _nodes = MutableStateFlow<Map<Int, NodeInfo>>(mapOf())
|
||||
val nodes: StateFlow<Map<Int, NodeInfo>> = _nodes
|
||||
val nodes: StateFlow<Map<Int, NodeInfo>> get() = radioConfigRepository.nodeDBbyNum
|
||||
|
||||
private val _myNodeInfo = MutableStateFlow<MyNodeInfo?>(null)
|
||||
val myNodeInfo get() = _myNodeInfo
|
||||
|
|
@ -86,21 +81,10 @@ class RadioConfigViewModel @Inject constructor(
|
|||
val currentDeviceProfile get() = _currentDeviceProfile.value
|
||||
|
||||
init {
|
||||
radioInterfaceService.connectionState.onEach { state ->
|
||||
_connectionState.value = when {
|
||||
state.isConnected -> ConnectionState.CONNECTED
|
||||
else -> ConnectionState.DISCONNECTED
|
||||
}
|
||||
}.launchIn(viewModelScope)
|
||||
|
||||
radioConfigRepository.myNodeInfoFlow().onEach {
|
||||
_myNodeInfo.value = it
|
||||
}.launchIn(viewModelScope)
|
||||
|
||||
radioConfigRepository.nodeInfoFlow().onEach { list ->
|
||||
_nodes.value = list.associateBy { it.num }
|
||||
}.launchIn(viewModelScope)
|
||||
|
||||
radioConfigRepository.deviceProfileFlow.onEach {
|
||||
_currentDeviceProfile.value = it
|
||||
}.launchIn(viewModelScope)
|
||||
|
|
@ -114,8 +98,8 @@ class RadioConfigViewModel @Inject constructor(
|
|||
debug("RadioConfigViewModel created")
|
||||
}
|
||||
|
||||
val myNodeNum get() = _myNodeInfo.value?.myNodeNum
|
||||
val maxChannels get() = _myNodeInfo.value?.maxChannels ?: 8
|
||||
val myNodeNum get() = myNodeInfo.value?.myNodeNum
|
||||
val maxChannels get() = myNodeInfo.value?.maxChannels ?: 8
|
||||
private val ourNodeInfo: NodeInfo? get() = nodes.value[myNodeNum]
|
||||
|
||||
override fun onCleared() {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class UIViewModel @Inject constructor(
|
|||
|
||||
var actionBarMenu: Menu? = null
|
||||
val meshService: IMeshService? get() = radioConfigRepository.meshService
|
||||
val nodeDB = NodeDB(this)
|
||||
val nodeDB = radioConfigRepository.nodeDB
|
||||
|
||||
val bondedAddress get() = radioInterfaceService.getBondedDeviceAddress()
|
||||
val selectedBluetooth get() = radioInterfaceService.getDeviceAddress()?.getOrNull(0) == 'x'
|
||||
|
|
@ -138,6 +138,10 @@ class UIViewModel @Inject constructor(
|
|||
private val _quickChatActions = MutableStateFlow<List<QuickChatAction>>(emptyList())
|
||||
val quickChatActions: StateFlow<List<QuickChatAction>> = _quickChatActions
|
||||
|
||||
// hardware info about our local device (can be null)
|
||||
private val _myNodeInfo = MutableStateFlow<MyNodeInfo?>(null)
|
||||
val myNodeInfo: StateFlow<MyNodeInfo?> get() = _myNodeInfo
|
||||
|
||||
private val _ourNodeInfo = MutableStateFlow<NodeInfo?>(null)
|
||||
val ourNodeInfo: StateFlow<NodeInfo?> = _ourNodeInfo
|
||||
|
||||
|
|
@ -152,6 +156,10 @@ class UIViewModel @Inject constructor(
|
|||
radioInterfaceService.clearErrorMessage()
|
||||
}.launchIn(viewModelScope)
|
||||
|
||||
radioConfigRepository.myNodeInfoFlow().onEach {
|
||||
_myNodeInfo.value = it
|
||||
}.launchIn(viewModelScope)
|
||||
|
||||
radioConfigRepository.nodeInfoFlow().onEach(nodeDB::setNodes)
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
|
|
@ -356,15 +364,8 @@ class UIViewModel @Inject constructor(
|
|||
// managed mode disables all access to configuration
|
||||
val isManaged: Boolean get() = config.device.isManaged
|
||||
|
||||
/// hardware info about our local device (can be null)
|
||||
private val _myNodeInfo = MutableLiveData<MyNodeInfo?>()
|
||||
val myNodeInfo: LiveData<MyNodeInfo?> get() = _myNodeInfo
|
||||
val myNodeNum get() = _myNodeInfo.value?.myNodeNum
|
||||
val maxChannels = myNodeInfo.value?.maxChannels ?: 8
|
||||
|
||||
fun setMyNodeInfo(info: MyNodeInfo?) {
|
||||
_myNodeInfo.value = info
|
||||
}
|
||||
val myNodeNum get() = myNodeInfo.value?.myNodeNum
|
||||
val maxChannels get() = myNodeInfo.value?.maxChannels ?: 8
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
|
@ -445,7 +446,7 @@ class UIViewModel @Inject constructor(
|
|||
val myNodeNum = myNodeNum ?: return@launch
|
||||
|
||||
// Capture the current node value while we're still on main thread
|
||||
val nodes = nodeDB.nodes.value ?: emptyMap()
|
||||
val nodes = nodeDB.nodes.value
|
||||
|
||||
val positionToPos: (MeshProtos.Position?) -> Position? = { meshPosition ->
|
||||
meshPosition?.let { Position(it) }.takeIf {
|
||||
|
|
@ -602,7 +603,7 @@ class UIViewModel @Inject constructor(
|
|||
|
||||
if (data?.portnumValue == Portnums.PortNum.TRACEROUTE_APP_VALUE) {
|
||||
val parsed = MeshProtos.RouteDiscovery.parseFrom(data.payload)
|
||||
fun nodeName(num: Int) = nodeDB.nodesByNum?.get(num)?.user?.longName
|
||||
fun nodeName(num: Int) = nodeDB.nodesByNum[num]?.user?.longName
|
||||
?: app.getString(R.string.unknown_username)
|
||||
|
||||
_tracerouteResponse.value = buildString {
|
||||
|
|
|
|||
|
|
@ -11,66 +11,52 @@ import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
|||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig
|
||||
import com.geeksville.mesh.MyNodeInfo
|
||||
import com.geeksville.mesh.NodeInfo
|
||||
import com.geeksville.mesh.database.dao.MyNodeInfoDao
|
||||
import com.geeksville.mesh.database.dao.NodeInfoDao
|
||||
import com.geeksville.mesh.deviceProfile
|
||||
import com.geeksville.mesh.model.NodeDB
|
||||
import com.geeksville.mesh.model.getChannelUrl
|
||||
import com.geeksville.mesh.service.MeshService.ConnectionState
|
||||
import com.geeksville.mesh.service.ServiceRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Class responsible for radio configuration data.
|
||||
* Combines access to [MyNodeInfo] & [NodeInfo] Room databases
|
||||
* and [ChannelSet], [LocalConfig] & [LocalModuleConfig] data stores.
|
||||
* Combines access to [nodeDB], [ChannelSet], [LocalConfig] & [LocalModuleConfig].
|
||||
*/
|
||||
class RadioConfigRepository @Inject constructor(
|
||||
private val serviceRepository: ServiceRepository,
|
||||
private val myNodeInfoDao: MyNodeInfoDao,
|
||||
private val nodeInfoDao: NodeInfoDao,
|
||||
val nodeDB: NodeDB,
|
||||
private val channelSetRepository: ChannelSetRepository,
|
||||
private val localConfigRepository: LocalConfigRepository,
|
||||
private val moduleConfigRepository: ModuleConfigRepository,
|
||||
) {
|
||||
val meshService: IMeshService? get() = serviceRepository.meshService
|
||||
|
||||
suspend fun clearNodeDB() = withContext(Dispatchers.IO) {
|
||||
myNodeInfoDao.clearMyNodeInfo()
|
||||
nodeInfoDao.clearNodeInfo()
|
||||
}
|
||||
// Connection state to our radio device
|
||||
val connectionState get() = serviceRepository.connectionState
|
||||
fun setConnectionState(state: ConnectionState) = serviceRepository.setConnectionState(state)
|
||||
|
||||
/**
|
||||
* Flow representing the [MyNodeInfo] database.
|
||||
*/
|
||||
fun myNodeInfoFlow(): Flow<MyNodeInfo?> = myNodeInfoDao.getMyNodeInfo()
|
||||
fun myNodeInfoFlow(): Flow<MyNodeInfo?> = nodeDB.myNodeInfoFlow()
|
||||
suspend fun getMyNodeInfo(): MyNodeInfo? = myNodeInfoFlow().firstOrNull()
|
||||
|
||||
suspend fun setMyNodeInfo(myInfo: MyNodeInfo?) = withContext(Dispatchers.IO) {
|
||||
myNodeInfoDao.setMyNodeInfo(myInfo)
|
||||
}
|
||||
val nodeDBbyNum: StateFlow<Map<Int, NodeInfo>> get() = nodeDB.nodeDBbyNum
|
||||
val nodeDBbyID: StateFlow<Map<String, NodeInfo>> get() = nodeDB.nodes
|
||||
|
||||
/**
|
||||
* Flow representing the [NodeInfo] database.
|
||||
*/
|
||||
fun nodeInfoFlow(): Flow<List<NodeInfo>> = nodeInfoDao.getNodes()
|
||||
fun nodeInfoFlow(): Flow<List<NodeInfo>> = nodeDB.nodeInfoFlow()
|
||||
suspend fun getNodes(): List<NodeInfo>? = nodeInfoFlow().firstOrNull()
|
||||
|
||||
suspend fun upsert(node: NodeInfo) = withContext(Dispatchers.IO) {
|
||||
nodeInfoDao.upsert(node)
|
||||
}
|
||||
|
||||
suspend fun putAll(nodes: List<NodeInfo>) = withContext(Dispatchers.IO) {
|
||||
nodeInfoDao.putAll(nodes)
|
||||
}
|
||||
|
||||
suspend fun installNodeDB(mi: MyNodeInfo?, nodes: List<NodeInfo>) {
|
||||
clearNodeDB()
|
||||
putAll(nodes)
|
||||
setMyNodeInfo(mi) // set MyNodeInfo last
|
||||
suspend fun upsert(node: NodeInfo) = nodeDB.upsert(node)
|
||||
suspend fun installNodeDB(mi: MyNodeInfo, nodes: List<NodeInfo>) {
|
||||
nodeDB.installNodeDB(mi, nodes)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -149,13 +135,13 @@ class RadioConfigRepository @Inject constructor(
|
|||
*/
|
||||
val deviceProfileFlow: Flow<DeviceProfile> = combine(
|
||||
myNodeInfoFlow(),
|
||||
nodeInfoFlow(),
|
||||
nodeDBbyNum,
|
||||
channelSetFlow,
|
||||
localConfigFlow,
|
||||
moduleConfigFlow,
|
||||
) { myInfo, nodes, channels, localConfig, localModuleConfig ->
|
||||
deviceProfile {
|
||||
nodes.firstOrNull { it.num == myInfo?.myNodeNum }?.user?.let {
|
||||
nodes[myInfo?.myNodeNum]?.user?.let {
|
||||
longName = it.longName
|
||||
shortName = it.shortName
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,7 +144,9 @@ class MeshService : Service(), Logging {
|
|||
/// A mapping of receiver class name to package name - used for explicit broadcasts
|
||||
private val clientPackages = mutableMapOf<String, String>()
|
||||
private val serviceNotifications = MeshServiceNotifications(this)
|
||||
private val serviceBroadcasts = MeshServiceBroadcasts(this, clientPackages) { connectionState }
|
||||
private val serviceBroadcasts = MeshServiceBroadcasts(this, clientPackages) {
|
||||
connectionState.also { radioConfigRepository.setConnectionState(it) }
|
||||
}
|
||||
private val serviceJob = Job()
|
||||
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||
private var connectionState = ConnectionState.DISCONNECTED
|
||||
|
|
@ -1053,7 +1055,7 @@ class MeshService : Service(), Logging {
|
|||
)
|
||||
}
|
||||
|
||||
// Have our timeout fire in the approprate number of seconds
|
||||
// Have our timeout fire in the appropriate number of seconds
|
||||
sleepTimeout = serviceScope.handledLaunch {
|
||||
try {
|
||||
// If we have a valid timeout, wait that long (+30 seconds) otherwise, just wait 30 seconds
|
||||
|
|
@ -1432,9 +1434,9 @@ class MeshService : Service(), Logging {
|
|||
insertMeshLog(packetToSave)
|
||||
|
||||
// This was our config request
|
||||
if (newMyNodeInfo == null || newNodes.isEmpty())
|
||||
if (newMyNodeInfo == null || newNodes.isEmpty()) {
|
||||
errormsg("Did not receive a valid config")
|
||||
else {
|
||||
} else {
|
||||
discardNodeDB()
|
||||
debug("Installing new node DB")
|
||||
myNodeInfo = newMyNodeInfo // Install myNodeInfo as current
|
||||
|
|
@ -1448,7 +1450,7 @@ class MeshService : Service(), Logging {
|
|||
myNodeInfo = newMyNodeInfo // we might have just updated myNodeInfo
|
||||
|
||||
serviceScope.handledLaunch {
|
||||
radioConfigRepository.installNodeDB(newMyNodeInfo, nodeDBbyID.values.toList())
|
||||
radioConfigRepository.installNodeDB(newMyNodeInfo!!, nodeDBbyID.values.toList())
|
||||
}
|
||||
|
||||
sendAnalytics()
|
||||
|
|
@ -1460,8 +1462,9 @@ class MeshService : Service(), Logging {
|
|||
}
|
||||
onHasSettings()
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
warn("Ignoring stale config complete")
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestConfig(config: AdminProtos.AdminMessage.ConfigType) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
package com.geeksville.mesh.service
|
||||
|
||||
import com.geeksville.mesh.IMeshService
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Repository class for managing the [IMeshService] instance and connection state
|
||||
*/
|
||||
@Singleton
|
||||
class ServiceRepository @Inject constructor() {
|
||||
var meshService: IMeshService? = null
|
||||
|
|
@ -12,4 +17,12 @@ class ServiceRepository @Inject constructor() {
|
|||
fun setMeshService(service: IMeshService?) {
|
||||
meshService = service
|
||||
}
|
||||
|
||||
// Connection state to our radio device
|
||||
private val _connectionState = MutableStateFlow(MeshService.ConnectionState.DISCONNECTED)
|
||||
val connectionState: StateFlow<MeshService.ConnectionState> get() = _connectionState
|
||||
|
||||
fun setConnectionState(connectionState: MeshService.ConnectionState) {
|
||||
_connectionState.value = connectionState
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class ContactsFragment : ScreenFragment("Messages"), Logging {
|
|||
val toBroadcast = contact.to == DataPacket.ID_BROADCAST
|
||||
|
||||
// grab usernames from NodeInfo
|
||||
val nodes = model.nodeDB.nodes.value!!
|
||||
val nodes = model.nodeDB.nodes.value
|
||||
val node = nodes[if (fromLocal) contact.to else contact.from]
|
||||
|
||||
//grab channel names from DeviceConfig
|
||||
|
|
@ -212,7 +212,7 @@ class ContactsFragment : ScreenFragment("Messages"), Logging {
|
|||
contactsAdapter.onChannelsChanged()
|
||||
}
|
||||
|
||||
model.nodeDB.nodes.observe(viewLifecycleOwner) {
|
||||
model.nodeDB.nodes.asLiveData().observe(viewLifecycleOwner) {
|
||||
contactsAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class MessagesFragment : Fragment(), Logging {
|
|||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val packet = messages[position]
|
||||
val msg = packet.data
|
||||
val nodes = model.nodeDB.nodes.value!!
|
||||
val nodes = model.nodeDB.nodes.value
|
||||
val node = nodes[msg.from]
|
||||
// Determine if this is my message (originated on this device)
|
||||
val isLocal = msg.from == DataPacket.ID_LOCAL
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
}
|
||||
|
||||
// Also watch myNodeInfo because it might change later
|
||||
model.myNodeInfo.observe(viewLifecycleOwner) {
|
||||
model.myNodeInfo.asLiveData().observe(viewLifecycleOwner) {
|
||||
updateNodeInfo()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
|||
values
|
||||
}.toTypedArray()
|
||||
|
||||
model.nodeDB.nodes.observe(viewLifecycleOwner) {
|
||||
model.nodeDB.nodes.asLiveData().observe(viewLifecycleOwner) {
|
||||
nodesAdapter.onNodesChanged(it.perhapsReindexBy(model.myNodeNum))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.DataPacket
|
||||
|
|
@ -187,7 +188,7 @@ fun MapView(model: UIViewModel = viewModel()) {
|
|||
requestPermissionAndToggleLauncher.launch(context.getLocationPermissions())
|
||||
}
|
||||
|
||||
val nodes by model.nodeDB.nodes.observeAsState(emptyMap())
|
||||
val nodes by model.nodeDB.nodes.collectAsStateWithLifecycle()
|
||||
val waypoints by model.waypoints.observeAsState(emptyMap())
|
||||
|
||||
var showDownloadButton: Boolean by remember { mutableStateOf(false) }
|
||||
|
|
@ -256,8 +257,7 @@ fun MapView(model: UIViewModel = viewModel()) {
|
|||
}
|
||||
|
||||
fun getUsername(id: String?) = if (id == DataPacket.ID_LOCAL) context.getString(R.string.you)
|
||||
else model.nodeDB.nodes.value?.get(id)?.user?.longName
|
||||
?: context.getString(R.string.unknown_username)
|
||||
else model.nodeDB.nodes.value[id]?.user?.longName ?: context.getString(R.string.unknown_username)
|
||||
|
||||
fun MapView.onWaypointChanged(waypoints: Collection<Packet>): List<MarkerWithLabel> {
|
||||
return waypoints.mapNotNull { waypoint ->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue