mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-06-02 06:24:16 +02:00
Add core data modules (#3169)
This commit is contained in:
@@ -178,6 +178,8 @@ androidComponents {
|
|||||||
project.afterEvaluate { logger.lifecycle("Version code is set to: ${android.defaultConfig.versionCode}") }
|
project.afterEvaluate { logger.lifecycle("Version code is set to: ${android.defaultConfig.versionCode}") }
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(projects.core.data)
|
||||||
|
implementation(projects.core.datastore)
|
||||||
implementation(projects.core.model)
|
implementation(projects.core.model)
|
||||||
implementation(projects.core.navigation)
|
implementation(projects.core.navigation)
|
||||||
implementation(projects.core.network)
|
implementation(projects.core.network)
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ 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.repository.bluetooth.BluetoothRepository
|
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
|
||||||
import com.geeksville.mesh.repository.datastore.recentaddresses.RecentAddress
|
|
||||||
import com.geeksville.mesh.repository.datastore.recentaddresses.RecentAddressesRepository
|
|
||||||
import com.geeksville.mesh.repository.network.NetworkRepository
|
import com.geeksville.mesh.repository.network.NetworkRepository
|
||||||
import com.geeksville.mesh.repository.network.NetworkRepository.Companion.toAddressString
|
import com.geeksville.mesh.repository.network.NetworkRepository.Companion.toAddressString
|
||||||
import com.geeksville.mesh.repository.radio.InterfaceId
|
import com.geeksville.mesh.repository.radio.InterfaceId
|
||||||
@@ -52,6 +50,8 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||||
|
import org.meshtastic.core.datastore.model.RecentAddress
|
||||||
import org.meshtastic.core.strings.R
|
import org.meshtastic.core.strings.R
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ constructor(
|
|||||||
private val usbManagerLazy: dagger.Lazy<UsbManager>,
|
private val usbManagerLazy: dagger.Lazy<UsbManager>,
|
||||||
private val networkRepository: NetworkRepository,
|
private val networkRepository: NetworkRepository,
|
||||||
private val radioInterfaceService: RadioInterfaceService,
|
private val radioInterfaceService: RadioInterfaceService,
|
||||||
private val recentAddressesRepository: RecentAddressesRepository,
|
private val recentAddressesDataSource: RecentAddressesDataSource,
|
||||||
) : ViewModel(),
|
) : ViewModel(),
|
||||||
Logging {
|
Logging {
|
||||||
private val context: Context
|
private val context: Context
|
||||||
@@ -125,7 +125,7 @@ constructor(
|
|||||||
|
|
||||||
// Flow for discovered TCP devices, using recent addresses for potential name enrichment
|
// Flow for discovered TCP devices, using recent addresses for potential name enrichment
|
||||||
private val processedDiscoveredTcpDevicesFlow: StateFlow<List<DeviceListEntry.Tcp>> =
|
private val processedDiscoveredTcpDevicesFlow: StateFlow<List<DeviceListEntry.Tcp>> =
|
||||||
combine(networkRepository.resolvedList, recentAddressesRepository.recentAddresses) { tcpServices, recentList ->
|
combine(networkRepository.resolvedList, recentAddressesDataSource.recentAddresses) { tcpServices, recentList ->
|
||||||
val recentMap = recentList.associateBy({ it.address }, { it.name })
|
val recentMap = recentList.associateBy({ it.address }, { it.name })
|
||||||
tcpServices
|
tcpServices
|
||||||
.map { service ->
|
.map { service ->
|
||||||
@@ -149,7 +149,7 @@ constructor(
|
|||||||
|
|
||||||
// Flow for recent TCP devices, filtered to exclude any currently discovered devices
|
// Flow for recent TCP devices, filtered to exclude any currently discovered devices
|
||||||
private val filteredRecentTcpDevicesFlow: StateFlow<List<DeviceListEntry.Tcp>> =
|
private val filteredRecentTcpDevicesFlow: StateFlow<List<DeviceListEntry.Tcp>> =
|
||||||
combine(recentAddressesRepository.recentAddresses, processedDiscoveredTcpDevicesFlow) {
|
combine(recentAddressesDataSource.recentAddresses, processedDiscoveredTcpDevicesFlow) {
|
||||||
recentList,
|
recentList,
|
||||||
discoveredDevices,
|
discoveredDevices,
|
||||||
->
|
->
|
||||||
@@ -328,11 +328,11 @@ constructor(
|
|||||||
|
|
||||||
fun addRecentAddress(address: String, name: String) {
|
fun addRecentAddress(address: String, name: String) {
|
||||||
if (!address.startsWith("t")) return
|
if (!address.startsWith("t")) return
|
||||||
viewModelScope.launch { recentAddressesRepository.add(RecentAddress(address, name)) }
|
viewModelScope.launch { recentAddressesDataSource.add(RecentAddress(address, name)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeRecentAddress(address: String) {
|
fun removeRecentAddress(address: String) {
|
||||||
viewModelScope.launch { recentAddressesRepository.remove(address) }
|
viewModelScope.launch { recentAddressesDataSource.remove(address) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by the GUI when a new device has been selected by the user
|
// Called by the GUI when a new device has been selected by the user
|
||||||
|
|||||||
+18
-15
@@ -45,6 +45,9 @@ import kotlinx.coroutines.flow.SharedFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import org.meshtastic.core.datastore.ChannelSetDataSource
|
||||||
|
import org.meshtastic.core.datastore.LocalConfigDataSource
|
||||||
|
import org.meshtastic.core.datastore.ModuleConfigDataSource
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,9 +59,9 @@ class RadioConfigRepository
|
|||||||
constructor(
|
constructor(
|
||||||
private val serviceRepository: ServiceRepository,
|
private val serviceRepository: ServiceRepository,
|
||||||
private val nodeDB: NodeRepository,
|
private val nodeDB: NodeRepository,
|
||||||
private val channelSetRepository: ChannelSetRepository,
|
private val channelSetDataSource: ChannelSetDataSource,
|
||||||
private val localConfigRepository: LocalConfigRepository,
|
private val localConfigDataSource: LocalConfigDataSource,
|
||||||
private val moduleConfigRepository: ModuleConfigRepository,
|
private val moduleConfigDataSource: ModuleConfigDataSource,
|
||||||
) {
|
) {
|
||||||
val meshService: IMeshService?
|
val meshService: IMeshService?
|
||||||
get() = serviceRepository.meshService
|
get() = serviceRepository.meshService
|
||||||
@@ -104,17 +107,17 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Flow representing the [ChannelSet] data store. */
|
/** Flow representing the [ChannelSet] data store. */
|
||||||
val channelSetFlow: Flow<ChannelSet> = channelSetRepository.channelSetFlow
|
val channelSetFlow: Flow<ChannelSet> = channelSetDataSource.channelSetFlow
|
||||||
|
|
||||||
/** Clears the [ChannelSet] data in the data store. */
|
/** Clears the [ChannelSet] data in the data store. */
|
||||||
suspend fun clearChannelSet() {
|
suspend fun clearChannelSet() {
|
||||||
channelSetRepository.clearChannelSet()
|
channelSetDataSource.clearChannelSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Replaces the [ChannelSettings] list with a new [settingsList]. */
|
/** Replaces the [ChannelSettings] list with a new [settingsList]. */
|
||||||
suspend fun replaceAllSettings(settingsList: List<ChannelSettings>) {
|
suspend fun replaceAllSettings(settingsList: List<ChannelSettings>) {
|
||||||
channelSetRepository.clearSettings()
|
channelSetDataSource.clearSettings()
|
||||||
channelSetRepository.addAllSettings(settingsList)
|
channelSetDataSource.addAllSettings(settingsList)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,14 +127,14 @@ constructor(
|
|||||||
* @param channel The [Channel] provided.
|
* @param channel The [Channel] provided.
|
||||||
* @return the index of the admin channel after the update (if not found, returns 0).
|
* @return the index of the admin channel after the update (if not found, returns 0).
|
||||||
*/
|
*/
|
||||||
suspend fun updateChannelSettings(channel: Channel) = channelSetRepository.updateChannelSettings(channel)
|
suspend fun updateChannelSettings(channel: Channel) = channelSetDataSource.updateChannelSettings(channel)
|
||||||
|
|
||||||
/** Flow representing the [LocalConfig] data store. */
|
/** Flow representing the [LocalConfig] data store. */
|
||||||
val localConfigFlow: Flow<LocalConfig> = localConfigRepository.localConfigFlow
|
val localConfigFlow: Flow<LocalConfig> = localConfigDataSource.localConfigFlow
|
||||||
|
|
||||||
/** Clears the [LocalConfig] data in the data store. */
|
/** Clears the [LocalConfig] data in the data store. */
|
||||||
suspend fun clearLocalConfig() {
|
suspend fun clearLocalConfig() {
|
||||||
localConfigRepository.clearLocalConfig()
|
localConfigDataSource.clearLocalConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,16 +143,16 @@ constructor(
|
|||||||
* @param config The [Config] to be set.
|
* @param config The [Config] to be set.
|
||||||
*/
|
*/
|
||||||
suspend fun setLocalConfig(config: Config) {
|
suspend fun setLocalConfig(config: Config) {
|
||||||
localConfigRepository.setLocalConfig(config)
|
localConfigDataSource.setLocalConfig(config)
|
||||||
if (config.hasLora()) channelSetRepository.setLoraConfig(config.lora)
|
if (config.hasLora()) channelSetDataSource.setLoraConfig(config.lora)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flow representing the [LocalModuleConfig] data store. */
|
/** Flow representing the [LocalModuleConfig] data store. */
|
||||||
val moduleConfigFlow: Flow<LocalModuleConfig> = moduleConfigRepository.moduleConfigFlow
|
val moduleConfigFlow: Flow<LocalModuleConfig> = moduleConfigDataSource.moduleConfigFlow
|
||||||
|
|
||||||
/** Clears the [LocalModuleConfig] data in the data store. */
|
/** Clears the [LocalModuleConfig] data in the data store. */
|
||||||
suspend fun clearLocalModuleConfig() {
|
suspend fun clearLocalModuleConfig() {
|
||||||
moduleConfigRepository.clearLocalModuleConfig()
|
moduleConfigDataSource.clearLocalModuleConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,7 +161,7 @@ constructor(
|
|||||||
* @param config The [ModuleConfig] to be set.
|
* @param config The [ModuleConfig] to be set.
|
||||||
*/
|
*/
|
||||||
suspend fun setLocalModuleConfig(config: ModuleConfig) {
|
suspend fun setLocalModuleConfig(config: ModuleConfig) {
|
||||||
moduleConfigRepository.setLocalModuleConfig(config)
|
moduleConfigDataSource.setLocalModuleConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flow representing the combined [DeviceProfile] protobuf. */
|
/** Flow representing the combined [DeviceProfile] protobuf. */
|
||||||
|
|||||||
@@ -44,15 +44,14 @@ import javax.net.ssl.SSLContext
|
|||||||
import javax.net.ssl.TrustManager
|
import javax.net.ssl.TrustManager
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class MQTTRepository @Inject constructor(
|
class MQTTRepository @Inject constructor(private val radioConfigRepository: RadioConfigRepository) : Logging {
|
||||||
private val radioConfigRepository: RadioConfigRepository,
|
|
||||||
) : Logging {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Quality of Service (QoS) levels in MQTT:
|
* Quality of Service (QoS) levels in MQTT:
|
||||||
* - QoS 0: "at most once". Packets are sent once without validation if it has been received.
|
* - QoS 0: "at most once". Packets are sent once without validation if it has been received.
|
||||||
* - QoS 1: "at least once". Packets are sent and stored until the client receives confirmation from the server. MQTT ensures delivery, but duplicates may occur.
|
* - QoS 1: "at least once". Packets are sent and stored until the client receives confirmation from the server.
|
||||||
|
* MQTT ensures delivery, but duplicates may occur.
|
||||||
* - QoS 2: "exactly once". Similar to QoS 1, but with no duplicates.
|
* - QoS 2: "exactly once". Similar to QoS 1, but with no duplicates.
|
||||||
*/
|
*/
|
||||||
private const val DEFAULT_QOS = 1
|
private const val DEFAULT_QOS = 1
|
||||||
@@ -84,63 +83,72 @@ class MQTTRepository @Inject constructor(
|
|||||||
|
|
||||||
val rootTopic = mqttConfig.root.ifEmpty { DEFAULT_TOPIC_ROOT }
|
val rootTopic = mqttConfig.root.ifEmpty { DEFAULT_TOPIC_ROOT }
|
||||||
|
|
||||||
val connectOptions = MqttConnectOptions().apply {
|
val connectOptions =
|
||||||
userName = mqttConfig.username
|
MqttConnectOptions().apply {
|
||||||
password = mqttConfig.password.toCharArray()
|
userName = mqttConfig.username
|
||||||
isAutomaticReconnect = true
|
password = mqttConfig.password.toCharArray()
|
||||||
if (mqttConfig.tlsEnabled) {
|
isAutomaticReconnect = true
|
||||||
socketFactory = sslContext.socketFactory
|
if (mqttConfig.tlsEnabled) {
|
||||||
}
|
socketFactory = sslContext.socketFactory
|
||||||
}
|
|
||||||
|
|
||||||
val bufferOptions = DisconnectedBufferOptions().apply {
|
|
||||||
isBufferEnabled = true
|
|
||||||
bufferSize = 512
|
|
||||||
isPersistBuffer = false
|
|
||||||
isDeleteOldestMessages = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = object : MqttCallbackExtended {
|
|
||||||
override fun connectComplete(reconnect: Boolean, serverURI: String) {
|
|
||||||
info("MQTT connectComplete: $serverURI reconnect: $reconnect")
|
|
||||||
channelSet.subscribeList.ifEmpty { return }.forEach { globalId ->
|
|
||||||
subscribe("$rootTopic$DEFAULT_TOPIC_LEVEL$globalId/+")
|
|
||||||
if (mqttConfig.jsonEnabled) subscribe("$rootTopic$JSON_TOPIC_LEVEL$globalId/+")
|
|
||||||
}
|
}
|
||||||
subscribe("$rootTopic${DEFAULT_TOPIC_LEVEL}PKI/+")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun connectionLost(cause: Throwable) {
|
val bufferOptions =
|
||||||
info("MQTT connectionLost cause: $cause")
|
DisconnectedBufferOptions().apply {
|
||||||
if (cause is IllegalArgumentException) close(cause)
|
isBufferEnabled = true
|
||||||
|
bufferSize = 512
|
||||||
|
isPersistBuffer = false
|
||||||
|
isDeleteOldestMessages = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun messageArrived(topic: String, message: MqttMessage) {
|
val callback =
|
||||||
trySend(mqttClientProxyMessage {
|
object : MqttCallbackExtended {
|
||||||
this.topic = topic
|
override fun connectComplete(reconnect: Boolean, serverURI: String) {
|
||||||
data = ByteString.copyFrom(message.payload)
|
info("MQTT connectComplete: $serverURI reconnect: $reconnect")
|
||||||
retained = message.isRetained
|
channelSet.subscribeList
|
||||||
})
|
.ifEmpty {
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
.forEach { globalId ->
|
||||||
|
subscribe("$rootTopic$DEFAULT_TOPIC_LEVEL$globalId/+")
|
||||||
|
if (mqttConfig.jsonEnabled) subscribe("$rootTopic$JSON_TOPIC_LEVEL$globalId/+")
|
||||||
|
}
|
||||||
|
subscribe("$rootTopic${DEFAULT_TOPIC_LEVEL}PKI/+")
|
||||||
|
}
|
||||||
|
|
||||||
override fun deliveryComplete(token: IMqttDeliveryToken?) {
|
override fun connectionLost(cause: Throwable) {
|
||||||
info("MQTT deliveryComplete messageId: ${token?.messageId}")
|
info("MQTT connectionLost cause: $cause")
|
||||||
|
if (cause is IllegalArgumentException) close(cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun messageArrived(topic: String, message: MqttMessage) {
|
||||||
|
trySend(
|
||||||
|
mqttClientProxyMessage {
|
||||||
|
this.topic = topic
|
||||||
|
data = ByteString.copyFrom(message.payload)
|
||||||
|
retained = message.isRetained
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deliveryComplete(token: IMqttDeliveryToken?) {
|
||||||
|
info("MQTT deliveryComplete messageId: ${token?.messageId}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val scheme = if (mqttConfig.tlsEnabled) "ssl" else "tcp"
|
val scheme = if (mqttConfig.tlsEnabled) "ssl" else "tcp"
|
||||||
val (host, port) = mqttConfig.address.ifEmpty { DEFAULT_SERVER_ADDRESS }
|
val (host, port) =
|
||||||
.split(":", limit = 2).let { it[0] to (it.getOrNull(1)?.toIntOrNull() ?: -1) }
|
mqttConfig.address
|
||||||
|
.ifEmpty { DEFAULT_SERVER_ADDRESS }
|
||||||
|
.split(":", limit = 2)
|
||||||
|
.let { it[0] to (it.getOrNull(1)?.toIntOrNull() ?: -1) }
|
||||||
|
|
||||||
mqttClient = MqttAsyncClient(
|
mqttClient =
|
||||||
URI(scheme, null, host, port, "", "", "").toString(),
|
MqttAsyncClient(URI(scheme, null, host, port, "", "", "").toString(), ownerId, MemoryPersistence()).apply {
|
||||||
ownerId,
|
setCallback(callback)
|
||||||
MemoryPersistence(),
|
setBufferOpts(bufferOptions)
|
||||||
).apply {
|
connect(connectOptions)
|
||||||
setCallback(callback)
|
}
|
||||||
setBufferOpts(bufferOptions)
|
|
||||||
connect(connectOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
awaitClose { disconnect() }
|
awaitClose { disconnect() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ dependencies {
|
|||||||
kover(projects.app)
|
kover(projects.app)
|
||||||
kover(projects.meshServiceExample)
|
kover(projects.meshServiceExample)
|
||||||
|
|
||||||
|
kover(projects.core.data)
|
||||||
|
kover(projects.core.datastore)
|
||||||
kover(projects.core.model)
|
kover(projects.core.model)
|
||||||
kover(projects.core.navigation)
|
kover(projects.core.navigation)
|
||||||
kover(projects.core.network)
|
kover(projects.core.network)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.meshtastic.android.library)
|
||||||
|
alias(libs.plugins.kover)
|
||||||
|
}
|
||||||
|
|
||||||
|
android { namespace = "org.meshtastic.core.data" }
|
||||||
|
|
||||||
|
dependencies {}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.meshtastic.android.library)
|
||||||
|
alias(libs.plugins.meshtastic.hilt)
|
||||||
|
alias(libs.plugins.meshtastic.kotlinx.serialization)
|
||||||
|
alias(libs.plugins.kover)
|
||||||
|
}
|
||||||
|
|
||||||
|
android { namespace = "org.meshtastic.core.datastore" }
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.core.proto)
|
||||||
|
|
||||||
|
implementation(libs.bundles.datastore)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.timber)
|
||||||
|
}
|
||||||
+14
-26
@@ -15,30 +15,28 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore
|
package org.meshtastic.core.datastore
|
||||||
|
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import com.geeksville.mesh.android.Logging
|
|
||||||
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
||||||
import com.geeksville.mesh.ChannelProtos.Channel
|
import com.geeksville.mesh.ChannelProtos.Channel
|
||||||
import com.geeksville.mesh.ChannelProtos.ChannelSettings
|
import com.geeksville.mesh.ChannelProtos.ChannelSettings
|
||||||
import com.geeksville.mesh.ConfigProtos
|
import com.geeksville.mesh.ConfigProtos
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
/**
|
/** Class that handles saving and retrieving [ChannelSet] data. */
|
||||||
* Class that handles saving and retrieving [ChannelSet] data.
|
@Singleton
|
||||||
*/
|
class ChannelSetDataSource @Inject constructor(private val channelSetStore: DataStore<ChannelSet>) {
|
||||||
class ChannelSetRepository @Inject constructor(
|
val channelSetFlow: Flow<ChannelSet> =
|
||||||
private val channelSetStore: DataStore<ChannelSet>
|
channelSetStore.data.catch { exception ->
|
||||||
) : Logging {
|
|
||||||
val channelSetFlow: Flow<ChannelSet> = channelSetStore.data
|
|
||||||
.catch { exception ->
|
|
||||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||||
if (exception is IOException) {
|
if (exception is IOException) {
|
||||||
errormsg("Error reading DeviceConfig settings: ${exception.message}")
|
Timber.e("Error reading DeviceConfig settings: ${exception.message}")
|
||||||
emit(ChannelSet.getDefaultInstance())
|
emit(ChannelSet.getDefaultInstance())
|
||||||
} else {
|
} else {
|
||||||
throw exception
|
throw exception
|
||||||
@@ -46,26 +44,18 @@ class ChannelSetRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clearChannelSet() {
|
suspend fun clearChannelSet() {
|
||||||
channelSetStore.updateData { preference ->
|
channelSetStore.updateData { preference -> preference.toBuilder().clear().build() }
|
||||||
preference.toBuilder().clear().build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clearSettings() {
|
suspend fun clearSettings() {
|
||||||
channelSetStore.updateData { preference ->
|
channelSetStore.updateData { preference -> preference.toBuilder().clearSettings().build() }
|
||||||
preference.toBuilder().clearSettings().build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun addAllSettings(settingsList: List<ChannelSettings>) {
|
suspend fun addAllSettings(settingsList: List<ChannelSettings>) {
|
||||||
channelSetStore.updateData { preference ->
|
channelSetStore.updateData { preference -> preference.toBuilder().addAllSettings(settingsList).build() }
|
||||||
preference.toBuilder().addAllSettings(settingsList).build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Updates the [ChannelSettings] list with the provided channel. */
|
||||||
* Updates the [ChannelSettings] list with the provided channel.
|
|
||||||
*/
|
|
||||||
suspend fun updateChannelSettings(channel: Channel) {
|
suspend fun updateChannelSettings(channel: Channel) {
|
||||||
if (channel.role == Channel.Role.DISABLED) return
|
if (channel.role == Channel.Role.DISABLED) return
|
||||||
channelSetStore.updateData { preference ->
|
channelSetStore.updateData { preference ->
|
||||||
@@ -80,8 +70,6 @@ class ChannelSetRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setLoraConfig(config: ConfigProtos.Config.LoRaConfig) {
|
suspend fun setLoraConfig(config: ConfigProtos.Config.LoRaConfig) {
|
||||||
channelSetStore.updateData { preference ->
|
channelSetStore.updateData { preference -> preference.toBuilder().setLoraConfig(config).build() }
|
||||||
preference.toBuilder().setLoraConfig(config).build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+12
-18
@@ -15,28 +15,26 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore
|
package org.meshtastic.core.datastore
|
||||||
|
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import com.geeksville.mesh.android.Logging
|
|
||||||
import com.geeksville.mesh.ConfigProtos.Config
|
import com.geeksville.mesh.ConfigProtos.Config
|
||||||
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
/**
|
/** Class that handles saving and retrieving [LocalConfig] data. */
|
||||||
* Class that handles saving and retrieving [LocalConfig] data.
|
@Singleton
|
||||||
*/
|
class LocalConfigDataSource @Inject constructor(private val localConfigStore: DataStore<LocalConfig>) {
|
||||||
class LocalConfigRepository @Inject constructor(
|
val localConfigFlow: Flow<LocalConfig> =
|
||||||
private val localConfigStore: DataStore<LocalConfig>,
|
localConfigStore.data.catch { exception ->
|
||||||
) : Logging {
|
|
||||||
val localConfigFlow: Flow<LocalConfig> = localConfigStore.data
|
|
||||||
.catch { exception ->
|
|
||||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||||
if (exception is IOException) {
|
if (exception is IOException) {
|
||||||
errormsg("Error reading LocalConfig settings: ${exception.message}")
|
Timber.e("Error reading LocalConfig settings: ${exception.message}")
|
||||||
emit(LocalConfig.getDefaultInstance())
|
emit(LocalConfig.getDefaultInstance())
|
||||||
} else {
|
} else {
|
||||||
throw exception
|
throw exception
|
||||||
@@ -44,14 +42,10 @@ class LocalConfigRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clearLocalConfig() {
|
suspend fun clearLocalConfig() {
|
||||||
localConfigStore.updateData { preference ->
|
localConfigStore.updateData { preference -> preference.toBuilder().clear().build() }
|
||||||
preference.toBuilder().clear().build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Updates [LocalConfig] from each [Config] oneOf. */
|
||||||
* Updates [LocalConfig] from each [Config] oneOf.
|
|
||||||
*/
|
|
||||||
suspend fun setLocalConfig(config: Config) = localConfigStore.updateData {
|
suspend fun setLocalConfig(config: Config) = localConfigStore.updateData {
|
||||||
val builder = it.toBuilder()
|
val builder = it.toBuilder()
|
||||||
config.allFields.forEach { (field, value) ->
|
config.allFields.forEach { (field, value) ->
|
||||||
@@ -59,7 +53,7 @@ class LocalConfigRepository @Inject constructor(
|
|||||||
if (localField != null) {
|
if (localField != null) {
|
||||||
builder.setField(localField, value)
|
builder.setField(localField, value)
|
||||||
} else {
|
} else {
|
||||||
errormsg("Error writing LocalConfig settings: ${config.payloadVariantCase}")
|
Timber.e("Error writing LocalConfig settings: ${config.payloadVariantCase}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.build()
|
builder.build()
|
||||||
+13
-19
@@ -15,28 +15,26 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore
|
package org.meshtastic.core.datastore
|
||||||
|
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import com.geeksville.mesh.android.Logging
|
|
||||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig
|
|
||||||
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
||||||
|
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
/**
|
/** Class that handles saving and retrieving [LocalModuleConfig] data. */
|
||||||
* Class that handles saving and retrieving [LocalModuleConfig] data.
|
@Singleton
|
||||||
*/
|
class ModuleConfigDataSource @Inject constructor(private val moduleConfigStore: DataStore<LocalModuleConfig>) {
|
||||||
class ModuleConfigRepository @Inject constructor(
|
val moduleConfigFlow: Flow<LocalModuleConfig> =
|
||||||
private val moduleConfigStore: DataStore<LocalModuleConfig>,
|
moduleConfigStore.data.catch { exception ->
|
||||||
) : Logging {
|
|
||||||
val moduleConfigFlow: Flow<LocalModuleConfig> = moduleConfigStore.data
|
|
||||||
.catch { exception ->
|
|
||||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||||
if (exception is IOException) {
|
if (exception is IOException) {
|
||||||
errormsg("Error reading LocalModuleConfig settings: ${exception.message}")
|
Timber.e("Error reading LocalModuleConfig settings: ${exception.message}")
|
||||||
emit(LocalModuleConfig.getDefaultInstance())
|
emit(LocalModuleConfig.getDefaultInstance())
|
||||||
} else {
|
} else {
|
||||||
throw exception
|
throw exception
|
||||||
@@ -44,14 +42,10 @@ class ModuleConfigRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clearLocalModuleConfig() {
|
suspend fun clearLocalModuleConfig() {
|
||||||
moduleConfigStore.updateData { preference ->
|
moduleConfigStore.updateData { preference -> preference.toBuilder().clear().build() }
|
||||||
preference.toBuilder().clear().build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Updates [LocalModuleConfig] from each [ModuleConfig] oneOf. */
|
||||||
* Updates [LocalModuleConfig] from each [ModuleConfig] oneOf.
|
|
||||||
*/
|
|
||||||
suspend fun setLocalModuleConfig(config: ModuleConfig) = moduleConfigStore.updateData {
|
suspend fun setLocalModuleConfig(config: ModuleConfig) = moduleConfigStore.updateData {
|
||||||
val builder = it.toBuilder()
|
val builder = it.toBuilder()
|
||||||
config.allFields.forEach { (field, value) ->
|
config.allFields.forEach { (field, value) ->
|
||||||
@@ -59,7 +53,7 @@ class ModuleConfigRepository @Inject constructor(
|
|||||||
if (localField != null) {
|
if (localField != null) {
|
||||||
builder.setField(localField, value)
|
builder.setField(localField, value)
|
||||||
} else {
|
} else {
|
||||||
errormsg("Error writing LocalModuleConfig settings: ${config.payloadVariantCase}")
|
Timber.e("Error writing LocalModuleConfig settings: ${config.payloadVariantCase}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.build()
|
builder.build()
|
||||||
+8
-15
@@ -15,15 +15,12 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore.recentaddresses
|
package org.meshtastic.core.datastore
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import com.geeksville.mesh.android.Logging
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
@@ -31,17 +28,13 @@ import kotlinx.serialization.SerializationException
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.meshtastic.core.strings.R
|
import org.meshtastic.core.datastore.model.RecentAddress
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class RecentAddressesRepository
|
class RecentAddressesDataSource @Inject constructor(private val dataStore: DataStore<Preferences>) {
|
||||||
@Inject
|
|
||||||
constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val dataStore: DataStore<Preferences>,
|
|
||||||
) : Logging {
|
|
||||||
private object PreferencesKeys {
|
private object PreferencesKeys {
|
||||||
val RECENT_IP_ADDRESSES = stringPreferencesKey("recent-ip-addresses")
|
val RECENT_IP_ADDRESSES = stringPreferencesKey("recent-ip-addresses")
|
||||||
}
|
}
|
||||||
@@ -53,11 +46,11 @@ constructor(
|
|||||||
try {
|
try {
|
||||||
Json.decodeFromString<List<RecentAddress>>(jsonString)
|
Json.decodeFromString<List<RecentAddress>>(jsonString)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
warn("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
Timber.w("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
||||||
// Fallback to legacy parsing
|
// Fallback to legacy parsing
|
||||||
parseLegacyRecentAddresses(jsonString)
|
parseLegacyRecentAddresses(jsonString)
|
||||||
} catch (e: SerializationException) {
|
} catch (e: SerializationException) {
|
||||||
warn("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
Timber.w("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
||||||
// Fallback to legacy parsing
|
// Fallback to legacy parsing
|
||||||
parseLegacyRecentAddresses(jsonString)
|
parseLegacyRecentAddresses(jsonString)
|
||||||
}
|
}
|
||||||
@@ -76,11 +69,11 @@ constructor(
|
|||||||
}
|
}
|
||||||
is String -> {
|
is String -> {
|
||||||
// Old format: just the address string
|
// Old format: just the address string
|
||||||
RecentAddress(address = item, name = context.getString(R.string.meshtastic))
|
RecentAddress(address = item, name = "Meshtastic")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Unknown format, log or handle as an error if necessary
|
// Unknown format, log or handle as an error if necessary
|
||||||
warn("Unknown item type in recent IP addresses: $item")
|
Timber.w("Unknown item type in recent IP addresses: $item")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+12
-28
@@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore
|
package org.meshtastic.core.datastore.di
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
@@ -30,16 +30,18 @@ import androidx.datastore.preferences.preferencesDataStoreFile
|
|||||||
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
||||||
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
||||||
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
||||||
import com.geeksville.mesh.repository.datastore.recentaddresses.RecentAddressesRepository
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import javax.inject.Singleton
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import org.meshtastic.core.datastore.serializer.ChannelSetSerializer
|
||||||
|
import org.meshtastic.core.datastore.serializer.LocalConfigSerializer
|
||||||
|
import org.meshtastic.core.datastore.serializer.ModuleConfigSerializer
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
private const val USER_PREFERENCES_NAME = "user_preferences"
|
private const val USER_PREFERENCES_NAME = "user_preferences"
|
||||||
|
|
||||||
@@ -48,12 +50,9 @@ private const val USER_PREFERENCES_NAME = "user_preferences"
|
|||||||
object DataStoreModule {
|
object DataStoreModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun providePreferencesDataStore(
|
fun providePreferencesDataStore(@ApplicationContext appContext: Context): DataStore<Preferences> =
|
||||||
@ApplicationContext appContext: Context
|
|
||||||
): DataStore<Preferences> =
|
|
||||||
PreferenceDataStoreFactory.create(
|
PreferenceDataStoreFactory.create(
|
||||||
corruptionHandler =
|
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
|
||||||
ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
|
|
||||||
migrations = listOf(SharedPreferencesMigration(appContext, USER_PREFERENCES_NAME)),
|
migrations = listOf(SharedPreferencesMigration(appContext, USER_PREFERENCES_NAME)),
|
||||||
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
||||||
produceFile = { appContext.preferencesDataStoreFile(USER_PREFERENCES_NAME) },
|
produceFile = { appContext.preferencesDataStoreFile(USER_PREFERENCES_NAME) },
|
||||||
@@ -61,36 +60,22 @@ object DataStoreModule {
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideRecentAddressesRepository(
|
fun provideLocalConfigDataStore(@ApplicationContext appContext: Context): DataStore<LocalConfig> =
|
||||||
@ApplicationContext context: Context,
|
|
||||||
dataStore: DataStore<Preferences>,
|
|
||||||
): RecentAddressesRepository = RecentAddressesRepository(context, dataStore)
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideLocalConfigDataStore(
|
|
||||||
@ApplicationContext appContext: Context
|
|
||||||
): DataStore<LocalConfig> =
|
|
||||||
DataStoreFactory.create(
|
DataStoreFactory.create(
|
||||||
serializer = LocalConfigSerializer,
|
serializer = LocalConfigSerializer,
|
||||||
produceFile = { appContext.dataStoreFile("local_config.pb") },
|
produceFile = { appContext.dataStoreFile("local_config.pb") },
|
||||||
corruptionHandler =
|
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalConfig.getDefaultInstance() }),
|
||||||
ReplaceFileCorruptionHandler(produceNewData = { LocalConfig.getDefaultInstance() }),
|
|
||||||
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
||||||
)
|
)
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideModuleConfigDataStore(
|
fun provideModuleConfigDataStore(@ApplicationContext appContext: Context): DataStore<LocalModuleConfig> =
|
||||||
@ApplicationContext appContext: Context
|
|
||||||
): DataStore<LocalModuleConfig> =
|
|
||||||
DataStoreFactory.create(
|
DataStoreFactory.create(
|
||||||
serializer = ModuleConfigSerializer,
|
serializer = ModuleConfigSerializer,
|
||||||
produceFile = { appContext.dataStoreFile("module_config.pb") },
|
produceFile = { appContext.dataStoreFile("module_config.pb") },
|
||||||
corruptionHandler =
|
corruptionHandler =
|
||||||
ReplaceFileCorruptionHandler(
|
ReplaceFileCorruptionHandler(produceNewData = { LocalModuleConfig.getDefaultInstance() }),
|
||||||
produceNewData = { LocalModuleConfig.getDefaultInstance() }
|
|
||||||
),
|
|
||||||
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,8 +85,7 @@ object DataStoreModule {
|
|||||||
DataStoreFactory.create(
|
DataStoreFactory.create(
|
||||||
serializer = ChannelSetSerializer,
|
serializer = ChannelSetSerializer,
|
||||||
produceFile = { appContext.dataStoreFile("channel_set.pb") },
|
produceFile = { appContext.dataStoreFile("channel_set.pb") },
|
||||||
corruptionHandler =
|
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet.getDefaultInstance() }),
|
||||||
ReplaceFileCorruptionHandler(produceNewData = { ChannelSet.getDefaultInstance() }),
|
|
||||||
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore.recentaddresses
|
package org.meshtastic.core.datastore.model
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
+2
-4
@@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore
|
package org.meshtastic.core.datastore.serializer
|
||||||
|
|
||||||
import androidx.datastore.core.CorruptionException
|
import androidx.datastore.core.CorruptionException
|
||||||
import androidx.datastore.core.Serializer
|
import androidx.datastore.core.Serializer
|
||||||
@@ -24,9 +24,7 @@ import com.google.protobuf.InvalidProtocolBufferException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
/**
|
/** Serializer for the [ChannelSet] object defined in apponly.proto. */
|
||||||
* Serializer for the [ChannelSet] object defined in apponly.proto.
|
|
||||||
*/
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
object ChannelSetSerializer : Serializer<ChannelSet> {
|
object ChannelSetSerializer : Serializer<ChannelSet> {
|
||||||
override val defaultValue: ChannelSet = ChannelSet.getDefaultInstance()
|
override val defaultValue: ChannelSet = ChannelSet.getDefaultInstance()
|
||||||
+2
-4
@@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore
|
package org.meshtastic.core.datastore.serializer
|
||||||
|
|
||||||
import androidx.datastore.core.CorruptionException
|
import androidx.datastore.core.CorruptionException
|
||||||
import androidx.datastore.core.Serializer
|
import androidx.datastore.core.Serializer
|
||||||
@@ -24,9 +24,7 @@ import com.google.protobuf.InvalidProtocolBufferException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
/**
|
/** Serializer for the [LocalConfig] object defined in localonly.proto. */
|
||||||
* Serializer for the [LocalConfig] object defined in localonly.proto.
|
|
||||||
*/
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
object LocalConfigSerializer : Serializer<LocalConfig> {
|
object LocalConfigSerializer : Serializer<LocalConfig> {
|
||||||
override val defaultValue: LocalConfig = LocalConfig.getDefaultInstance()
|
override val defaultValue: LocalConfig = LocalConfig.getDefaultInstance()
|
||||||
+2
-4
@@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.repository.datastore
|
package org.meshtastic.core.datastore.serializer
|
||||||
|
|
||||||
import androidx.datastore.core.CorruptionException
|
import androidx.datastore.core.CorruptionException
|
||||||
import androidx.datastore.core.Serializer
|
import androidx.datastore.core.Serializer
|
||||||
@@ -24,9 +24,7 @@ import com.google.protobuf.InvalidProtocolBufferException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
/**
|
/** Serializer for the [LocalModuleConfig] object defined in localonly.proto. */
|
||||||
* Serializer for the [LocalModuleConfig] object defined in localonly.proto.
|
|
||||||
*/
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
object ModuleConfigSerializer : Serializer<LocalModuleConfig> {
|
object ModuleConfigSerializer : Serializer<LocalModuleConfig> {
|
||||||
override val defaultValue: LocalModuleConfig = LocalModuleConfig.getDefaultInstance()
|
override val defaultValue: LocalModuleConfig = LocalModuleConfig.getDefaultInstance()
|
||||||
+1
-1
@@ -17,7 +17,7 @@ import org.gradle.kotlin.dsl.maven
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
include(":app", ":core:model", ":core:navigation", ":core:network", ":core:prefs", ":core:proto",
|
include(":app", ":core:data", ":core:datastore", ":core:model", ":core:navigation", ":core:network", ":core:prefs", ":core:proto",
|
||||||
":core:strings", ":feature:map", ":mesh_service_example")
|
":core:strings", ":feature:map", ":mesh_service_example")
|
||||||
rootProject.name = "MeshtasticAndroid"
|
rootProject.name = "MeshtasticAndroid"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user