mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(wire): migrate from protobuf -> wire (#4401)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
9dbc8b7fbf
commit
25657e8f8f
239 changed files with 7149 additions and 6144 deletions
|
|
@ -20,11 +20,11 @@ import androidx.datastore.core.DataStore
|
|||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
|
||||
import org.meshtastic.proto.ChannelProtos.Channel
|
||||
import org.meshtastic.proto.ChannelProtos.ChannelSettings
|
||||
import org.meshtastic.proto.ConfigProtos
|
||||
import java.io.IOException
|
||||
import okio.IOException
|
||||
import org.meshtastic.proto.Channel
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import org.meshtastic.proto.ChannelSettings
|
||||
import org.meshtastic.proto.Config
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -36,38 +36,37 @@ class ChannelSetDataSource @Inject constructor(private val channelSetStore: Data
|
|||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
if (exception is IOException) {
|
||||
Logger.e { "Error reading DeviceConfig settings: ${exception.message}" }
|
||||
emit(ChannelSet.getDefaultInstance())
|
||||
emit(ChannelSet())
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearChannelSet() {
|
||||
channelSetStore.updateData { preference -> preference.toBuilder().clear().build() }
|
||||
channelSetStore.updateData { ChannelSet() }
|
||||
}
|
||||
|
||||
/** Replaces all [ChannelSettings] in a single atomic operation. */
|
||||
suspend fun replaceAllSettings(settingsList: List<ChannelSettings>) {
|
||||
channelSetStore.updateData { preference ->
|
||||
preference.toBuilder().clearSettings().addAllSettings(settingsList).build()
|
||||
}
|
||||
channelSetStore.updateData { it.copy(settings = settingsList) }
|
||||
}
|
||||
|
||||
/** Updates the [ChannelSettings] list with the provided channel. */
|
||||
suspend fun updateChannelSettings(channel: Channel) {
|
||||
if (channel.role == Channel.Role.DISABLED) return
|
||||
channelSetStore.updateData { preference ->
|
||||
val builder = preference.toBuilder()
|
||||
val settings = preference.settings.toMutableList()
|
||||
// Resize to fit channel
|
||||
while (builder.settingsCount <= channel.index) {
|
||||
builder.addSettings(ChannelSettings.getDefaultInstance())
|
||||
while (settings.size <= channel.index) {
|
||||
settings.add(ChannelSettings())
|
||||
}
|
||||
// use setSettings() to ensure settingsList and channel indexes match
|
||||
builder.setSettings(channel.index, channel.settings).build()
|
||||
settings[channel.index] = channel.settings ?: ChannelSettings()
|
||||
preference.copy(settings = settings)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setLoraConfig(config: ConfigProtos.Config.LoRaConfig) {
|
||||
channelSetStore.updateData { preference -> preference.toBuilder().setLoraConfig(config).build() }
|
||||
suspend fun setLoraConfig(config: Config.LoRaConfig) {
|
||||
channelSetStore.updateData { it.copy(lora_config = config) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ import androidx.datastore.core.DataStore
|
|||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import org.meshtastic.proto.ConfigProtos.Config
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import java.io.IOException
|
||||
import okio.IOException
|
||||
import org.meshtastic.proto.Config
|
||||
import org.meshtastic.proto.LocalConfig
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -34,28 +34,28 @@ class LocalConfigDataSource @Inject constructor(private val localConfigStore: Da
|
|||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
if (exception is IOException) {
|
||||
Logger.e { "Error reading LocalConfig settings: ${exception.message}" }
|
||||
emit(LocalConfig.getDefaultInstance())
|
||||
emit(LocalConfig())
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearLocalConfig() {
|
||||
localConfigStore.updateData { preference -> preference.toBuilder().clear().build() }
|
||||
localConfigStore.updateData { LocalConfig() }
|
||||
}
|
||||
|
||||
/** Updates [LocalConfig] from each [Config] oneOf. */
|
||||
suspend fun setLocalConfig(config: Config) = localConfigStore.updateData {
|
||||
val builder = it.toBuilder()
|
||||
config.allFields.forEach { (field, value) ->
|
||||
val localField = it.descriptorForType.findFieldByName(field.name)
|
||||
if (localField != null) {
|
||||
builder.setField(localField, value)
|
||||
} else {
|
||||
// Some fields like SESSIONKEY are not intended to be persisted in LocalConfig
|
||||
Logger.d { "Skipping non-persistent LocalConfig field: ${field.name}" }
|
||||
}
|
||||
suspend fun setLocalConfig(config: Config) = localConfigStore.updateData { current ->
|
||||
when {
|
||||
config.device != null -> current.copy(device = config.device)
|
||||
config.position != null -> current.copy(position = config.position)
|
||||
config.power != null -> current.copy(power = config.power)
|
||||
config.network != null -> current.copy(network = config.network)
|
||||
config.display != null -> current.copy(display = config.display)
|
||||
config.lora != null -> current.copy(lora = config.lora)
|
||||
config.bluetooth != null -> current.copy(bluetooth = config.bluetooth)
|
||||
config.security != null -> current.copy(security = config.security)
|
||||
else -> current
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 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
|
||||
|
|
@ -14,16 +14,15 @@
|
|||
* 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.datastore
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalModuleConfig
|
||||
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig
|
||||
import java.io.IOException
|
||||
import okio.IOException
|
||||
import org.meshtastic.proto.LocalModuleConfig
|
||||
import org.meshtastic.proto.ModuleConfig
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -35,27 +34,35 @@ class ModuleConfigDataSource @Inject constructor(private val moduleConfigStore:
|
|||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
if (exception is IOException) {
|
||||
Logger.e { "Error reading LocalModuleConfig settings: ${exception.message}" }
|
||||
emit(LocalModuleConfig.getDefaultInstance())
|
||||
emit(LocalModuleConfig())
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clearLocalModuleConfig() {
|
||||
moduleConfigStore.updateData { preference -> preference.toBuilder().clear().build() }
|
||||
moduleConfigStore.updateData { LocalModuleConfig() }
|
||||
}
|
||||
|
||||
/** Updates [LocalModuleConfig] from each [ModuleConfig] oneOf. */
|
||||
suspend fun setLocalModuleConfig(config: ModuleConfig) = moduleConfigStore.updateData {
|
||||
val builder = it.toBuilder()
|
||||
config.allFields.forEach { (field, value) ->
|
||||
val localField = it.descriptorForType.findFieldByName(field.name)
|
||||
if (localField != null) {
|
||||
builder.setField(localField, value)
|
||||
} else {
|
||||
Logger.e { "Error writing LocalModuleConfig settings: ${config.payloadVariantCase}" }
|
||||
}
|
||||
suspend fun setLocalModuleConfig(config: ModuleConfig) = moduleConfigStore.updateData { current ->
|
||||
when {
|
||||
config.mqtt != null -> current.copy(mqtt = config.mqtt)
|
||||
config.serial != null -> current.copy(serial = config.serial)
|
||||
config.external_notification != null ->
|
||||
current.copy(external_notification = config.external_notification)
|
||||
config.store_forward != null -> current.copy(store_forward = config.store_forward)
|
||||
config.range_test != null -> current.copy(range_test = config.range_test)
|
||||
config.telemetry != null -> current.copy(telemetry = config.telemetry)
|
||||
config.canned_message != null -> current.copy(canned_message = config.canned_message)
|
||||
config.audio != null -> current.copy(audio = config.audio)
|
||||
config.remote_hardware != null -> current.copy(remote_hardware = config.remote_hardware)
|
||||
config.neighbor_info != null -> current.copy(neighbor_info = config.neighbor_info)
|
||||
config.ambient_lighting != null -> current.copy(ambient_lighting = config.ambient_lighting)
|
||||
config.detection_sensor != null -> current.copy(detection_sensor = config.detection_sensor)
|
||||
config.paxcounter != null -> current.copy(paxcounter = config.paxcounter)
|
||||
config.statusmessage != null -> current.copy(statusmessage = config.statusmessage)
|
||||
else -> current
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 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
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* 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.datastore.di
|
||||
|
||||
import android.content.Context
|
||||
|
|
@ -45,9 +44,9 @@ import org.meshtastic.core.datastore.KEY_THEME
|
|||
import org.meshtastic.core.datastore.serializer.ChannelSetSerializer
|
||||
import org.meshtastic.core.datastore.serializer.LocalConfigSerializer
|
||||
import org.meshtastic.core.datastore.serializer.ModuleConfigSerializer
|
||||
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalModuleConfig
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import org.meshtastic.proto.LocalConfig
|
||||
import org.meshtastic.proto.LocalModuleConfig
|
||||
import javax.inject.Qualifier
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -103,7 +102,7 @@ object DataStoreModule {
|
|||
): DataStore<LocalConfig> = DataStoreFactory.create(
|
||||
serializer = LocalConfigSerializer,
|
||||
produceFile = { appContext.dataStoreFile("local_config.pb") },
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalConfig.getDefaultInstance() }),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalConfig() }),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
|
|
@ -115,8 +114,7 @@ object DataStoreModule {
|
|||
): DataStore<LocalModuleConfig> = DataStoreFactory.create(
|
||||
serializer = ModuleConfigSerializer,
|
||||
produceFile = { appContext.dataStoreFile("module_config.pb") },
|
||||
corruptionHandler =
|
||||
ReplaceFileCorruptionHandler(produceNewData = { LocalModuleConfig.getDefaultInstance() }),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalModuleConfig() }),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
|
|
@ -128,7 +126,7 @@ object DataStoreModule {
|
|||
): DataStore<ChannelSet> = DataStoreFactory.create(
|
||||
serializer = ChannelSetSerializer,
|
||||
produceFile = { appContext.dataStoreFile("channel_set.pb") },
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet.getDefaultInstance() }),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet() }),
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 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
|
||||
|
|
@ -14,28 +14,27 @@
|
|||
* 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.datastore.serializer
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
|
||||
import okio.IOException
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
/** Serializer for the [ChannelSet] object defined in apponly.proto. */
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
object ChannelSetSerializer : Serializer<ChannelSet> {
|
||||
override val defaultValue: ChannelSet = ChannelSet.getDefaultInstance()
|
||||
override val defaultValue: ChannelSet = ChannelSet()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): ChannelSet {
|
||||
try {
|
||||
return ChannelSet.parseFrom(input)
|
||||
} catch (exception: InvalidProtocolBufferException) {
|
||||
return ChannelSet.ADAPTER.decode(input)
|
||||
} catch (exception: IOException) {
|
||||
throw CorruptionException("Cannot read proto.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: ChannelSet, output: OutputStream) = t.writeTo(output)
|
||||
override suspend fun writeTo(t: ChannelSet, output: OutputStream) = ChannelSet.ADAPTER.encode(output, t)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 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
|
||||
|
|
@ -14,28 +14,27 @@
|
|||
* 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.datastore.serializer
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import okio.IOException
|
||||
import org.meshtastic.proto.LocalConfig
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
/** Serializer for the [LocalConfig] object defined in localonly.proto. */
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
object LocalConfigSerializer : Serializer<LocalConfig> {
|
||||
override val defaultValue: LocalConfig = LocalConfig.getDefaultInstance()
|
||||
override val defaultValue: LocalConfig = LocalConfig()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): LocalConfig {
|
||||
try {
|
||||
return LocalConfig.parseFrom(input)
|
||||
} catch (exception: InvalidProtocolBufferException) {
|
||||
return LocalConfig.ADAPTER.decode(input)
|
||||
} catch (exception: IOException) {
|
||||
throw CorruptionException("Cannot read proto.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: LocalConfig, output: OutputStream) = t.writeTo(output)
|
||||
override suspend fun writeTo(t: LocalConfig, output: OutputStream) = LocalConfig.ADAPTER.encode(output, t)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 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
|
||||
|
|
@ -14,28 +14,28 @@
|
|||
* 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.datastore.serializer
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalModuleConfig
|
||||
import okio.IOException
|
||||
import org.meshtastic.proto.LocalModuleConfig
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
/** Serializer for the [LocalModuleConfig] object defined in localonly.proto. */
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
object ModuleConfigSerializer : Serializer<LocalModuleConfig> {
|
||||
override val defaultValue: LocalModuleConfig = LocalModuleConfig.getDefaultInstance()
|
||||
override val defaultValue: LocalModuleConfig = LocalModuleConfig()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): LocalModuleConfig {
|
||||
try {
|
||||
return LocalModuleConfig.parseFrom(input)
|
||||
} catch (exception: InvalidProtocolBufferException) {
|
||||
return LocalModuleConfig.ADAPTER.decode(input)
|
||||
} catch (exception: IOException) {
|
||||
throw CorruptionException("Cannot read proto.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: LocalModuleConfig, output: OutputStream) = t.writeTo(output)
|
||||
override suspend fun writeTo(t: LocalModuleConfig, output: OutputStream) =
|
||||
LocalModuleConfig.ADAPTER.encode(output, t)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue