mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor: migrate preferences to DataStore and decouple core:domain for KMP (#4731)
This commit is contained in:
parent
87fdaa26ff
commit
b9b68d2779
113 changed files with 1790 additions and 1320 deletions
|
|
@ -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
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
|
|
@ -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
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
|
|
@ -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
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
|
|
@ -33,16 +32,16 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
internal const val KEY_APP_INTRO_COMPLETED = "app_intro_completed"
|
||||
internal const val KEY_THEME = "theme"
|
||||
const val KEY_APP_INTRO_COMPLETED = "app_intro_completed"
|
||||
const val KEY_THEME = "theme"
|
||||
|
||||
// Node list filters/sort
|
||||
internal const val KEY_NODE_SORT = "node-sort-option"
|
||||
internal const val KEY_INCLUDE_UNKNOWN = "include-unknown"
|
||||
internal const val KEY_EXCLUDE_INFRASTRUCTURE = "exclude-infrastructure"
|
||||
internal const val KEY_ONLY_ONLINE = "only-online"
|
||||
internal const val KEY_ONLY_DIRECT = "only-direct"
|
||||
internal const val KEY_SHOW_IGNORED = "show-ignored"
|
||||
const val KEY_NODE_SORT = "node-sort-option"
|
||||
const val KEY_INCLUDE_UNKNOWN = "include-unknown"
|
||||
const val KEY_EXCLUDE_INFRASTRUCTURE = "exclude-infrastructure"
|
||||
const val KEY_ONLY_ONLINE = "only-online"
|
||||
const val KEY_ONLY_DIRECT = "only-direct"
|
||||
const val KEY_SHOW_IGNORED = "show-ignored"
|
||||
|
||||
@Singleton
|
||||
class UiPreferencesDataSource @Inject constructor(private val dataStore: DataStore<Preferences>) {
|
||||
|
|
@ -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.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
|
@ -17,24 +17,25 @@
|
|||
package org.meshtastic.core.datastore.serializer
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import androidx.datastore.core.okio.OkioSerializer
|
||||
import okio.BufferedSink
|
||||
import okio.BufferedSource
|
||||
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> {
|
||||
object ChannelSetSerializer : OkioSerializer<ChannelSet> {
|
||||
override val defaultValue: ChannelSet = ChannelSet()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): ChannelSet {
|
||||
override suspend fun readFrom(source: BufferedSource): ChannelSet {
|
||||
try {
|
||||
return ChannelSet.ADAPTER.decode(input)
|
||||
return ChannelSet.ADAPTER.decode(source)
|
||||
} catch (exception: IOException) {
|
||||
throw CorruptionException("Cannot read proto.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: ChannelSet, output: OutputStream) = ChannelSet.ADAPTER.encode(output, t)
|
||||
override suspend fun writeTo(t: ChannelSet, sink: BufferedSink) {
|
||||
ChannelSet.ADAPTER.encode(sink, t)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,24 +17,25 @@
|
|||
package org.meshtastic.core.datastore.serializer
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import androidx.datastore.core.okio.OkioSerializer
|
||||
import okio.BufferedSink
|
||||
import okio.BufferedSource
|
||||
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> {
|
||||
object LocalConfigSerializer : OkioSerializer<LocalConfig> {
|
||||
override val defaultValue: LocalConfig = LocalConfig()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): LocalConfig {
|
||||
override suspend fun readFrom(source: BufferedSource): LocalConfig {
|
||||
try {
|
||||
return LocalConfig.ADAPTER.decode(input)
|
||||
return LocalConfig.ADAPTER.decode(source)
|
||||
} catch (exception: IOException) {
|
||||
throw CorruptionException("Cannot read proto.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: LocalConfig, output: OutputStream) = LocalConfig.ADAPTER.encode(output, t)
|
||||
override suspend fun writeTo(t: LocalConfig, sink: BufferedSink) {
|
||||
LocalConfig.ADAPTER.encode(sink, t)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,24 +17,25 @@
|
|||
package org.meshtastic.core.datastore.serializer
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import androidx.datastore.core.okio.OkioSerializer
|
||||
import okio.BufferedSink
|
||||
import okio.BufferedSource
|
||||
import okio.IOException
|
||||
import org.meshtastic.proto.LocalStats
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
/** Serializer for the [LocalStats] object defined in telemetry.proto. */
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
object LocalStatsSerializer : Serializer<LocalStats> {
|
||||
object LocalStatsSerializer : OkioSerializer<LocalStats> {
|
||||
override val defaultValue: LocalStats = LocalStats()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): LocalStats {
|
||||
override suspend fun readFrom(source: BufferedSource): LocalStats {
|
||||
try {
|
||||
return LocalStats.ADAPTER.decode(input)
|
||||
return LocalStats.ADAPTER.decode(source)
|
||||
} catch (exception: IOException) {
|
||||
throw CorruptionException("Cannot read proto.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: LocalStats, output: OutputStream) = LocalStats.ADAPTER.encode(output, t)
|
||||
override suspend fun writeTo(t: LocalStats, sink: BufferedSink) {
|
||||
LocalStats.ADAPTER.encode(sink, t)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,25 +17,25 @@
|
|||
package org.meshtastic.core.datastore.serializer
|
||||
|
||||
import androidx.datastore.core.CorruptionException
|
||||
import androidx.datastore.core.Serializer
|
||||
import androidx.datastore.core.okio.OkioSerializer
|
||||
import okio.BufferedSink
|
||||
import okio.BufferedSource
|
||||
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> {
|
||||
object ModuleConfigSerializer : OkioSerializer<LocalModuleConfig> {
|
||||
override val defaultValue: LocalModuleConfig = LocalModuleConfig()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): LocalModuleConfig {
|
||||
override suspend fun readFrom(source: BufferedSource): LocalModuleConfig {
|
||||
try {
|
||||
return LocalModuleConfig.ADAPTER.decode(input)
|
||||
return LocalModuleConfig.ADAPTER.decode(source)
|
||||
} catch (exception: IOException) {
|
||||
throw CorruptionException("Cannot read proto.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: LocalModuleConfig, output: OutputStream) =
|
||||
LocalModuleConfig.ADAPTER.encode(output, t)
|
||||
override suspend fun writeTo(t: LocalModuleConfig, sink: BufferedSink) {
|
||||
LocalModuleConfig.ADAPTER.encode(sink, t)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
* 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.datastore.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.core.DataStoreFactory
|
||||
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
|
||||
import androidx.datastore.dataStoreFile
|
||||
import androidx.datastore.preferences.SharedPreferencesMigration
|
||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.emptyPreferences
|
||||
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import org.meshtastic.core.datastore.KEY_APP_INTRO_COMPLETED
|
||||
import org.meshtastic.core.datastore.KEY_INCLUDE_UNKNOWN
|
||||
import org.meshtastic.core.datastore.KEY_NODE_SORT
|
||||
import org.meshtastic.core.datastore.KEY_ONLY_DIRECT
|
||||
import org.meshtastic.core.datastore.KEY_ONLY_ONLINE
|
||||
import org.meshtastic.core.datastore.KEY_SHOW_IGNORED
|
||||
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.LocalStatsSerializer
|
||||
import org.meshtastic.core.datastore.serializer.ModuleConfigSerializer
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import org.meshtastic.proto.LocalConfig
|
||||
import org.meshtastic.proto.LocalModuleConfig
|
||||
import org.meshtastic.proto.LocalStats
|
||||
import javax.inject.Qualifier
|
||||
import javax.inject.Singleton
|
||||
|
||||
private const val USER_PREFERENCES_NAME = "user_preferences"
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Qualifier
|
||||
annotation class DataStoreScope
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Module
|
||||
object DataStoreModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@DataStoreScope
|
||||
fun provideDataStoreScope(): CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun providePreferencesDataStore(
|
||||
@ApplicationContext appContext: Context,
|
||||
@DataStoreScope scope: CoroutineScope,
|
||||
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
|
||||
migrations =
|
||||
listOf(
|
||||
SharedPreferencesMigration(context = appContext, sharedPreferencesName = USER_PREFERENCES_NAME),
|
||||
SharedPreferencesMigration(
|
||||
context = appContext,
|
||||
sharedPreferencesName = "ui-prefs",
|
||||
keysToMigrate =
|
||||
setOf(
|
||||
KEY_APP_INTRO_COMPLETED,
|
||||
KEY_THEME,
|
||||
KEY_NODE_SORT,
|
||||
KEY_INCLUDE_UNKNOWN,
|
||||
KEY_ONLY_ONLINE,
|
||||
KEY_ONLY_DIRECT,
|
||||
KEY_SHOW_IGNORED,
|
||||
),
|
||||
),
|
||||
),
|
||||
scope = scope,
|
||||
produceFile = { appContext.preferencesDataStoreFile(USER_PREFERENCES_NAME) },
|
||||
)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideLocalConfigDataStore(
|
||||
@ApplicationContext appContext: Context,
|
||||
@DataStoreScope scope: CoroutineScope,
|
||||
): DataStore<LocalConfig> = DataStoreFactory.create(
|
||||
serializer = LocalConfigSerializer,
|
||||
produceFile = { appContext.dataStoreFile("local_config.pb") },
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalConfig() }),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideModuleConfigDataStore(
|
||||
@ApplicationContext appContext: Context,
|
||||
@DataStoreScope scope: CoroutineScope,
|
||||
): DataStore<LocalModuleConfig> = DataStoreFactory.create(
|
||||
serializer = ModuleConfigSerializer,
|
||||
produceFile = { appContext.dataStoreFile("module_config.pb") },
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalModuleConfig() }),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideChannelSetDataStore(
|
||||
@ApplicationContext appContext: Context,
|
||||
@DataStoreScope scope: CoroutineScope,
|
||||
): DataStore<ChannelSet> = DataStoreFactory.create(
|
||||
serializer = ChannelSetSerializer,
|
||||
produceFile = { appContext.dataStoreFile("channel_set.pb") },
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet() }),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideLocalStatsDataStore(
|
||||
@ApplicationContext appContext: Context,
|
||||
@DataStoreScope scope: CoroutineScope,
|
||||
): DataStore<LocalStats> = DataStoreFactory.create(
|
||||
serializer = LocalStatsSerializer,
|
||||
produceFile = { appContext.dataStoreFile("local_stats.pb") },
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalStats() }),
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue