mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: per device DB manager (#3641)
This commit is contained in:
parent
f0b9a0ff75
commit
cb8d1871c9
16 changed files with 643 additions and 86 deletions
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.data.datasource
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.meshtastic.core.database.entity.MyNodeEntity
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
import org.meshtastic.core.database.entity.NodeWithRelations
|
||||
|
||||
interface NodeInfoReadDataSource {
|
||||
fun myNodeInfoFlow(): Flow<MyNodeEntity?>
|
||||
|
||||
fun nodeDBbyNumFlow(): Flow<Map<Int, NodeWithRelations>>
|
||||
|
||||
fun getNodesFlow(
|
||||
sort: String,
|
||||
filter: String,
|
||||
includeUnknown: Boolean,
|
||||
hopsAwayMax: Int,
|
||||
lastHeardMin: Int,
|
||||
): Flow<List<NodeWithRelations>>
|
||||
|
||||
suspend fun getNodesOlderThan(lastHeard: Int): List<NodeEntity>
|
||||
|
||||
suspend fun getUnknownNodes(): List<NodeEntity>
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.data.datasource
|
||||
|
||||
import org.meshtastic.core.database.entity.MetadataEntity
|
||||
import org.meshtastic.core.database.entity.MyNodeEntity
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
|
||||
interface NodeInfoWriteDataSource {
|
||||
suspend fun upsert(node: NodeEntity)
|
||||
|
||||
suspend fun installConfig(mi: MyNodeEntity, nodes: List<NodeEntity>)
|
||||
|
||||
suspend fun clearNodeDB()
|
||||
|
||||
suspend fun deleteNode(num: Int)
|
||||
|
||||
suspend fun deleteNodes(nodeNums: List<Int>)
|
||||
|
||||
suspend fun deleteMetadata(num: Int)
|
||||
|
||||
suspend fun upsert(metadata: MetadataEntity)
|
||||
|
||||
suspend fun setNodeNotes(num: Int, notes: String)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.data.datasource
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.MyNodeEntity
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
import org.meshtastic.core.database.entity.NodeWithRelations
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class SwitchingNodeInfoReadDataSource @Inject constructor(private val dbManager: DatabaseManager) :
|
||||
NodeInfoReadDataSource {
|
||||
|
||||
override fun myNodeInfoFlow(): Flow<MyNodeEntity?> =
|
||||
dbManager.currentDb.flatMapLatest { db -> db.nodeInfoDao().getMyNodeInfo() }
|
||||
|
||||
override fun nodeDBbyNumFlow(): Flow<Map<Int, NodeWithRelations>> =
|
||||
dbManager.currentDb.flatMapLatest { db -> db.nodeInfoDao().nodeDBbyNum() }
|
||||
|
||||
override fun getNodesFlow(
|
||||
sort: String,
|
||||
filter: String,
|
||||
includeUnknown: Boolean,
|
||||
hopsAwayMax: Int,
|
||||
lastHeardMin: Int,
|
||||
): Flow<List<NodeWithRelations>> = dbManager.currentDb.flatMapLatest { db ->
|
||||
db.nodeInfoDao()
|
||||
.getNodes(
|
||||
sort = sort,
|
||||
filter = filter,
|
||||
includeUnknown = includeUnknown,
|
||||
hopsAwayMax = hopsAwayMax,
|
||||
lastHeardMin = lastHeardMin,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getNodesOlderThan(lastHeard: Int): List<NodeEntity> =
|
||||
dbManager.withDb { it.nodeInfoDao().getNodesOlderThan(lastHeard) }
|
||||
|
||||
override suspend fun getUnknownNodes(): List<NodeEntity> = dbManager.withDb { it.nodeInfoDao().getUnknownNodes() }
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.data.datasource
|
||||
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.MetadataEntity
|
||||
import org.meshtastic.core.database.entity.MyNodeEntity
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class SwitchingNodeInfoWriteDataSource
|
||||
@Inject
|
||||
constructor(
|
||||
private val dbManager: DatabaseManager,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) : NodeInfoWriteDataSource {
|
||||
|
||||
override suspend fun upsert(node: NodeEntity) =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().upsert(node) } }
|
||||
|
||||
override suspend fun installConfig(mi: MyNodeEntity, nodes: List<NodeEntity>) =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().installConfig(mi, nodes) } }
|
||||
|
||||
override suspend fun clearNodeDB() =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().clearNodeInfo() } }
|
||||
|
||||
override suspend fun deleteNode(num: Int) =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().deleteNode(num) } }
|
||||
|
||||
override suspend fun deleteNodes(nodeNums: List<Int>) =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().deleteNodes(nodeNums) } }
|
||||
|
||||
override suspend fun deleteMetadata(num: Int) =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().deleteMetadata(num) } }
|
||||
|
||||
override suspend fun upsert(metadata: MetadataEntity) =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().upsert(metadata) } }
|
||||
|
||||
override suspend fun setNodeNotes(num: Int, notes: String) =
|
||||
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().setNodeNotes(num, notes) } }
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.core.data.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import org.meshtastic.core.data.datasource.NodeInfoReadDataSource
|
||||
import org.meshtastic.core.data.datasource.NodeInfoWriteDataSource
|
||||
import org.meshtastic.core.data.datasource.SwitchingNodeInfoReadDataSource
|
||||
import org.meshtastic.core.data.datasource.SwitchingNodeInfoWriteDataSource
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface NodeDataSourceModule {
|
||||
@Binds @Singleton
|
||||
fun bindNodeInfoReadDataSource(impl: SwitchingNodeInfoReadDataSource): NodeInfoReadDataSource
|
||||
|
||||
@Binds @Singleton
|
||||
fun bindNodeInfoWriteDataSource(impl: SwitchingNodeInfoWriteDataSource): NodeInfoWriteDataSource
|
||||
}
|
||||
|
|
@ -17,14 +17,14 @@
|
|||
|
||||
package org.meshtastic.core.data.repository
|
||||
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.database.dao.MeshLogDao
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.MeshLog
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
|
|
@ -37,16 +37,16 @@ import javax.inject.Inject
|
|||
class MeshLogRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val meshLogDaoLazy: Lazy<MeshLogDao>,
|
||||
private val dbManager: DatabaseManager,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
private val meshLogDao by lazy { meshLogDaoLazy.get() }
|
||||
|
||||
fun getAllLogs(maxItems: Int = MAX_ITEMS): Flow<List<MeshLog>> =
|
||||
meshLogDao.getAllLogs(maxItems).flowOn(dispatchers.io).conflate()
|
||||
dbManager.currentDb.flatMapLatest { it.meshLogDao().getAllLogs(maxItems) }.flowOn(dispatchers.io).conflate()
|
||||
|
||||
fun getAllLogsInReceiveOrder(maxItems: Int = MAX_ITEMS): Flow<List<MeshLog>> =
|
||||
meshLogDao.getAllLogsInReceiveOrder(maxItems).flowOn(dispatchers.io).conflate()
|
||||
fun getAllLogsInReceiveOrder(maxItems: Int = MAX_ITEMS): Flow<List<MeshLog>> = dbManager.currentDb
|
||||
.flatMapLatest { it.meshLogDao().getAllLogsInReceiveOrder(maxItems) }
|
||||
.flowOn(dispatchers.io)
|
||||
.conflate()
|
||||
|
||||
private fun parseTelemetryLog(log: MeshLog): Telemetry? = runCatching {
|
||||
Telemetry.parseFrom(log.fromRadio.packet.decoded.payload)
|
||||
|
|
@ -106,8 +106,10 @@ constructor(
|
|||
}
|
||||
.getOrNull()
|
||||
|
||||
fun getTelemetryFrom(nodeNum: Int): Flow<List<Telemetry>> = meshLogDao
|
||||
.getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS)
|
||||
fun getTelemetryFrom(nodeNum: Int): Flow<List<Telemetry>> = dbManager.currentDb
|
||||
.flatMapLatest {
|
||||
it.meshLogDao().getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.mapLatest { list -> list.mapNotNull(::parseTelemetryLog) }
|
||||
.flowOn(dispatchers.io)
|
||||
|
|
@ -116,8 +118,10 @@ constructor(
|
|||
nodeNum: Int,
|
||||
portNum: Int = Portnums.PortNum.UNKNOWN_APP_VALUE,
|
||||
maxItem: Int = MAX_MESH_PACKETS,
|
||||
): Flow<List<MeshLog>> =
|
||||
meshLogDao.getLogsFrom(nodeNum, portNum, maxItem).distinctUntilChanged().flowOn(dispatchers.io)
|
||||
): Flow<List<MeshLog>> = dbManager.currentDb
|
||||
.flatMapLatest { it.meshLogDao().getLogsFrom(nodeNum, portNum, maxItem) }
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatchers.io)
|
||||
|
||||
/*
|
||||
* Retrieves MeshPackets matching 'nodeNum' and 'portNum'.
|
||||
|
|
@ -130,14 +134,16 @@ constructor(
|
|||
.mapLatest { list -> list.firstOrNull { it.myNodeInfo != null }?.myNodeInfo }
|
||||
.flowOn(dispatchers.io)
|
||||
|
||||
suspend fun insert(log: MeshLog) = withContext(dispatchers.io) { meshLogDao.insert(log) }
|
||||
suspend fun insert(log: MeshLog) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().insert(log) }
|
||||
|
||||
suspend fun deleteAll() = withContext(dispatchers.io) { meshLogDao.deleteAll() }
|
||||
suspend fun deleteAll() = withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().deleteAll() }
|
||||
|
||||
suspend fun deleteLog(uuid: String) = withContext(dispatchers.io) { meshLogDao.deleteLog(uuid) }
|
||||
suspend fun deleteLog(uuid: String) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().deleteLog(uuid) }
|
||||
|
||||
suspend fun deleteLogs(nodeNum: Int, portNum: Int) =
|
||||
withContext(dispatchers.io) { meshLogDao.deleteLogs(nodeNum, portNum) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().deleteLogs(nodeNum, portNum) }
|
||||
|
||||
companion object {
|
||||
private const val MAX_ITEMS = 500
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ import kotlinx.coroutines.flow.mapLatest
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.database.dao.NodeInfoDao
|
||||
import org.meshtastic.core.data.datasource.NodeInfoReadDataSource
|
||||
import org.meshtastic.core.data.datasource.NodeInfoWriteDataSource
|
||||
import org.meshtastic.core.database.entity.MetadataEntity
|
||||
import org.meshtastic.core.database.entity.MyNodeEntity
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
|
|
@ -49,13 +50,14 @@ class NodeRepository
|
|||
@Inject
|
||||
constructor(
|
||||
processLifecycle: Lifecycle,
|
||||
private val nodeInfoDao: NodeInfoDao,
|
||||
private val nodeInfoReadDataSource: NodeInfoReadDataSource,
|
||||
private val nodeInfoWriteDataSource: NodeInfoWriteDataSource,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
// hardware info about our local device (can be null)
|
||||
val myNodeInfo: StateFlow<MyNodeEntity?> =
|
||||
nodeInfoDao
|
||||
.getMyNodeInfo()
|
||||
nodeInfoReadDataSource
|
||||
.myNodeInfoFlow()
|
||||
.flowOn(dispatchers.io)
|
||||
.stateIn(processLifecycle.coroutineScope, SharingStarted.Eagerly, null)
|
||||
|
||||
|
|
@ -69,12 +71,13 @@ constructor(
|
|||
val myId: StateFlow<String?>
|
||||
get() = _myId
|
||||
|
||||
fun getNodeDBbyNum() = nodeInfoDao.nodeDBbyNum().map { map -> map.mapValues { (_, it) -> it.toEntity() } }
|
||||
fun getNodeDBbyNum() =
|
||||
nodeInfoReadDataSource.nodeDBbyNumFlow().map { map -> map.mapValues { (_, it) -> it.toEntity() } }
|
||||
|
||||
// A map from nodeNum to Node
|
||||
val nodeDBbyNum: StateFlow<Map<Int, Node>> =
|
||||
nodeInfoDao
|
||||
.nodeDBbyNum()
|
||||
nodeInfoReadDataSource
|
||||
.nodeDBbyNumFlow()
|
||||
.mapLatest { map -> map.mapValues { (_, it) -> it.toModel() } }
|
||||
.onEach {
|
||||
val ourNodeInfo = it.values.firstOrNull()
|
||||
|
|
@ -116,8 +119,8 @@ constructor(
|
|||
includeUnknown: Boolean = true,
|
||||
onlyOnline: Boolean = false,
|
||||
onlyDirect: Boolean = false,
|
||||
) = nodeInfoDao
|
||||
.getNodes(
|
||||
) = nodeInfoReadDataSource
|
||||
.getNodesFlow(
|
||||
sort = sort.sqlValue,
|
||||
filter = filter,
|
||||
includeUnknown = includeUnknown,
|
||||
|
|
@ -128,40 +131,46 @@ constructor(
|
|||
.flowOn(dispatchers.io)
|
||||
.conflate()
|
||||
|
||||
suspend fun upsert(node: NodeEntity) = withContext(dispatchers.io) { nodeInfoDao.upsert(node) }
|
||||
suspend fun upsert(node: NodeEntity) = withContext(dispatchers.io) { nodeInfoWriteDataSource.upsert(node) }
|
||||
|
||||
suspend fun installConfig(mi: MyNodeEntity, nodes: List<NodeEntity>) =
|
||||
withContext(dispatchers.io) { nodeInfoDao.installConfig(mi, nodes) }
|
||||
withContext(dispatchers.io) { nodeInfoWriteDataSource.installConfig(mi, nodes) }
|
||||
|
||||
suspend fun clearNodeDB() = withContext(dispatchers.io) { nodeInfoDao.clearNodeInfo() }
|
||||
suspend fun clearNodeDB() = withContext(dispatchers.io) { nodeInfoWriteDataSource.clearNodeDB() }
|
||||
|
||||
suspend fun deleteNode(num: Int) = withContext(dispatchers.io) {
|
||||
nodeInfoDao.deleteNode(num)
|
||||
nodeInfoDao.deleteMetadata(num)
|
||||
nodeInfoWriteDataSource.deleteNode(num)
|
||||
nodeInfoWriteDataSource.deleteMetadata(num)
|
||||
}
|
||||
|
||||
suspend fun deleteNodes(nodeNums: List<Int>) = withContext(dispatchers.io) {
|
||||
nodeInfoDao.deleteNodes(nodeNums)
|
||||
nodeNums.forEach { nodeInfoDao.deleteMetadata(it) }
|
||||
nodeInfoWriteDataSource.deleteNodes(nodeNums)
|
||||
nodeNums.forEach { nodeInfoWriteDataSource.deleteMetadata(it) }
|
||||
}
|
||||
|
||||
suspend fun getNodesOlderThan(lastHeard: Int): List<NodeEntity> =
|
||||
withContext(dispatchers.io) { nodeInfoDao.getNodesOlderThan(lastHeard) }
|
||||
withContext(dispatchers.io) { nodeInfoReadDataSource.getNodesOlderThan(lastHeard) }
|
||||
|
||||
suspend fun getUnknownNodes(): List<NodeEntity> = withContext(dispatchers.io) { nodeInfoDao.getUnknownNodes() }
|
||||
suspend fun getUnknownNodes(): List<NodeEntity> =
|
||||
withContext(dispatchers.io) { nodeInfoReadDataSource.getUnknownNodes() }
|
||||
|
||||
suspend fun insertMetadata(metadata: MetadataEntity) = withContext(dispatchers.io) { nodeInfoDao.upsert(metadata) }
|
||||
suspend fun insertMetadata(metadata: MetadataEntity) =
|
||||
withContext(dispatchers.io) { nodeInfoWriteDataSource.upsert(metadata) }
|
||||
|
||||
val onlineNodeCount: Flow<Int> =
|
||||
nodeInfoDao
|
||||
.nodeDBbyNum()
|
||||
nodeInfoReadDataSource
|
||||
.nodeDBbyNumFlow()
|
||||
.mapLatest { map -> map.values.count { it.node.lastHeard > onlineTimeThreshold() } }
|
||||
.flowOn(dispatchers.io)
|
||||
.conflate()
|
||||
|
||||
val totalNodeCount: Flow<Int> =
|
||||
nodeInfoDao.nodeDBbyNum().mapLatest { map -> map.values.count() }.flowOn(dispatchers.io).conflate()
|
||||
nodeInfoReadDataSource
|
||||
.nodeDBbyNumFlow()
|
||||
.mapLatest { map -> map.values.count() }
|
||||
.flowOn(dispatchers.io)
|
||||
.conflate()
|
||||
|
||||
suspend fun setNodeNotes(num: Int, notes: String) =
|
||||
withContext(dispatchers.io) { nodeInfoDao.setNodeNotes(num, notes) }
|
||||
withContext(dispatchers.io) { nodeInfoWriteDataSource.setNodeNotes(num, notes) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,45 +17,54 @@
|
|||
|
||||
package org.meshtastic.core.data.repository
|
||||
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.database.dao.PacketDao
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.ContactSettings
|
||||
import org.meshtastic.core.database.entity.Packet
|
||||
import org.meshtastic.core.database.entity.ReactionEntity
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.MessageStatus
|
||||
import org.meshtastic.proto.ChannelProtos.ChannelSettings
|
||||
import org.meshtastic.proto.Portnums.PortNum
|
||||
import javax.inject.Inject
|
||||
|
||||
class PacketRepository @Inject constructor(private val packetDaoLazy: Lazy<PacketDao>) {
|
||||
private val packetDao by lazy { packetDaoLazy.get() }
|
||||
class PacketRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val dbManager: DatabaseManager,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
fun getWaypoints(): Flow<List<Packet>> =
|
||||
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getAllPackets(PortNum.WAYPOINT_APP_VALUE) }
|
||||
|
||||
fun getWaypoints(): Flow<List<Packet>> = packetDao.getAllPackets(PortNum.WAYPOINT_APP_VALUE)
|
||||
|
||||
fun getContacts(): Flow<Map<String, Packet>> = packetDao.getContactKeys()
|
||||
fun getContacts(): Flow<Map<String, Packet>> =
|
||||
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getContactKeys() }
|
||||
|
||||
suspend fun getMessageCount(contact: String): Int =
|
||||
withContext(Dispatchers.IO) { packetDao.getMessageCount(contact) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getMessageCount(contact) }
|
||||
|
||||
suspend fun getUnreadCount(contact: String): Int = withContext(Dispatchers.IO) { packetDao.getUnreadCount(contact) }
|
||||
suspend fun getUnreadCount(contact: String): Int =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getUnreadCount(contact) }
|
||||
|
||||
fun getUnreadCountTotal(): Flow<Int> = packetDao.getUnreadCountTotal()
|
||||
fun getUnreadCountTotal(): Flow<Int> =
|
||||
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getUnreadCountTotal() }
|
||||
|
||||
suspend fun clearUnreadCount(contact: String, timestamp: Long) =
|
||||
withContext(Dispatchers.IO) { packetDao.clearUnreadCount(contact, timestamp) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().clearUnreadCount(contact, timestamp) }
|
||||
|
||||
suspend fun getQueuedPackets(): List<DataPacket>? = withContext(Dispatchers.IO) { packetDao.getQueuedPackets() }
|
||||
suspend fun getQueuedPackets(): List<DataPacket>? =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getQueuedPackets() }
|
||||
|
||||
suspend fun insert(packet: Packet) = withContext(Dispatchers.IO) { packetDao.insert(packet) }
|
||||
suspend fun insert(packet: Packet) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().insert(packet) }
|
||||
|
||||
suspend fun getMessagesFrom(contact: String, getNode: suspend (String?) -> Node) = withContext(Dispatchers.IO) {
|
||||
packetDao.getMessagesFrom(contact).mapLatest { packets ->
|
||||
suspend fun getMessagesFrom(contact: String, getNode: suspend (String?) -> Node) = withContext(dispatchers.io) {
|
||||
dbManager.currentDb.value.packetDao().getMessagesFrom(contact).mapLatest { packets ->
|
||||
packets.map { packet ->
|
||||
val message = packet.toMessage(getNode)
|
||||
message.replyId
|
||||
|
|
@ -68,43 +77,53 @@ class PacketRepository @Inject constructor(private val packetDaoLazy: Lazy<Packe
|
|||
}
|
||||
|
||||
suspend fun updateMessageStatus(d: DataPacket, m: MessageStatus) =
|
||||
withContext(Dispatchers.IO) { packetDao.updateMessageStatus(d, m) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().updateMessageStatus(d, m) }
|
||||
|
||||
suspend fun updateMessageId(d: DataPacket, id: Int) =
|
||||
withContext(Dispatchers.IO) { packetDao.updateMessageId(d, id) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().updateMessageId(d, id) }
|
||||
|
||||
suspend fun getPacketById(requestId: Int) = withContext(Dispatchers.IO) { packetDao.getPacketById(requestId) }
|
||||
suspend fun getPacketById(requestId: Int) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getPacketById(requestId) }
|
||||
|
||||
suspend fun getPacketByPacketId(packetId: Int) =
|
||||
withContext(Dispatchers.IO) { packetDao.getPacketByPacketId(packetId) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getPacketByPacketId(packetId) }
|
||||
|
||||
suspend fun deleteMessages(uuidList: List<Long>) = withContext(Dispatchers.IO) {
|
||||
for (chunk in uuidList.chunked(500)) { // limit number of UUIDs per query
|
||||
packetDao.deleteMessages(chunk)
|
||||
suspend fun deleteMessages(uuidList: List<Long>) = withContext(dispatchers.io) {
|
||||
for (chunk in uuidList.chunked(500)) {
|
||||
// Fetch DAO per chunk to avoid holding a stale reference if the active DB switches
|
||||
dbManager.currentDb.value.packetDao().deleteMessages(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteContacts(contactList: List<String>) =
|
||||
withContext(Dispatchers.IO) { packetDao.deleteContacts(contactList) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().deleteContacts(contactList) }
|
||||
|
||||
suspend fun deleteWaypoint(id: Int) = withContext(Dispatchers.IO) { packetDao.deleteWaypoint(id) }
|
||||
suspend fun deleteWaypoint(id: Int) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().deleteWaypoint(id) }
|
||||
|
||||
suspend fun delete(packet: Packet) = withContext(Dispatchers.IO) { packetDao.delete(packet) }
|
||||
suspend fun delete(packet: Packet) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().delete(packet) }
|
||||
|
||||
suspend fun update(packet: Packet) = withContext(Dispatchers.IO) { packetDao.update(packet) }
|
||||
suspend fun update(packet: Packet) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().update(packet) }
|
||||
|
||||
fun getContactSettings(): Flow<Map<String, ContactSettings>> = packetDao.getContactSettings()
|
||||
fun getContactSettings(): Flow<Map<String, ContactSettings>> =
|
||||
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getContactSettings() }
|
||||
|
||||
suspend fun getContactSettings(contact: String) =
|
||||
withContext(Dispatchers.IO) { packetDao.getContactSettings(contact) ?: ContactSettings(contact) }
|
||||
suspend fun getContactSettings(contact: String) = withContext(dispatchers.io) {
|
||||
dbManager.currentDb.value.packetDao().getContactSettings(contact) ?: ContactSettings(contact)
|
||||
}
|
||||
|
||||
suspend fun setMuteUntil(contacts: List<String>, until: Long) =
|
||||
withContext(Dispatchers.IO) { packetDao.setMuteUntil(contacts, until) }
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().setMuteUntil(contacts, until) }
|
||||
|
||||
suspend fun insertReaction(reaction: ReactionEntity) = withContext(Dispatchers.IO) { packetDao.insert(reaction) }
|
||||
suspend fun insertReaction(reaction: ReactionEntity) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().insert(reaction) }
|
||||
|
||||
suspend fun clearPacketDB() = withContext(Dispatchers.IO) { packetDao.deleteAll() }
|
||||
suspend fun clearPacketDB() = withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().deleteAll() }
|
||||
|
||||
suspend fun migrateChannelsByPSK(oldSettings: List<ChannelSettings>, newSettings: List<ChannelSettings>) =
|
||||
packetDao.migrateChannelsByPSK(oldSettings, newSettings)
|
||||
withContext(dispatchers.io) {
|
||||
dbManager.currentDb.value.packetDao().migrateChannelsByPSK(oldSettings, newSettings)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@
|
|||
|
||||
package org.meshtastic.core.data.repository
|
||||
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.database.dao.QuickChatActionDao
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.QuickChatAction
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import javax.inject.Inject
|
||||
|
|
@ -28,19 +28,20 @@ import javax.inject.Inject
|
|||
class QuickChatActionRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val quickChatDaoLazy: Lazy<QuickChatActionDao>,
|
||||
private val dbManager: DatabaseManager,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
private val quickChatActionDao by lazy { quickChatDaoLazy.get() }
|
||||
fun getAllActions() = dbManager.currentDb.flatMapLatest { it.quickChatActionDao().getAll() }.flowOn(dispatchers.io)
|
||||
|
||||
fun getAllActions() = quickChatActionDao.getAll().flowOn(dispatchers.io)
|
||||
suspend fun upsert(action: QuickChatAction) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.quickChatActionDao().upsert(action) }
|
||||
|
||||
suspend fun upsert(action: QuickChatAction) = withContext(dispatchers.io) { quickChatActionDao.upsert(action) }
|
||||
suspend fun deleteAll() = withContext(dispatchers.io) { dbManager.currentDb.value.quickChatActionDao().deleteAll() }
|
||||
|
||||
suspend fun deleteAll() = withContext(dispatchers.io) { quickChatActionDao.deleteAll() }
|
||||
suspend fun delete(action: QuickChatAction) =
|
||||
withContext(dispatchers.io) { dbManager.currentDb.value.quickChatActionDao().delete(action) }
|
||||
|
||||
suspend fun delete(action: QuickChatAction) = withContext(dispatchers.io) { quickChatActionDao.delete(action) }
|
||||
|
||||
suspend fun setItemPosition(uuid: Long, newPos: Int) =
|
||||
withContext(dispatchers.io) { quickChatActionDao.updateActionPosition(uuid, newPos) }
|
||||
suspend fun setItemPosition(uuid: Long, newPos: Int) = withContext(dispatchers.io) {
|
||||
dbManager.currentDb.value.quickChatActionDao().updateActionPosition(uuid, newPos)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue