refactor: migrate preferences to DataStore and decouple core:domain for KMP#4731
refactor: migrate preferences to DataStore and decouple core:domain for KMP#4731jamesarich merged 5 commits intomainfrom
Conversation
- Convert the `core:datastore` module to a Kotlin Multiplatform (KMP) configuration. - Relocate data sources, models, and serializers in `core:datastore` to `commonMain` and update serializers to implement `OkioSerializer`. - Extract `MeshLogRepository` into a platform-agnostic interface in `core:repository` and move the concrete implementation to `MeshLogRepositoryImpl` in `core:data`. - Move the `DatabaseManager` interface from `core:repository` to `core:common:database`. - Update the `core:domain` module configuration to remove the direct dependency on `core:data`, favoring repository interfaces. - Update package imports and dependency injections across multiple features (node, map, settings) and internal managers to reflect the new repository and database manager locations. Signed-off-by: James Rich <[email protected]>
…itory - Move preference interfaces from `core:prefs` to `core:repository` to decouple storage implementation from domain logic. - Transition preference properties to reactive `StateFlow` and explicit setter methods (e.g., `setLoggingEnabled`) to support better observability. - Introduce `PreferenceFlows.kt` to provide a utility for converting `SharedPreferences` listeners into Kotlin Flows. - Update ViewModels, UseCases, and Repository implementations across the project to use the new reactive preference APIs. - Consolidate common preference interfaces into an `AppPreferences` contract. - Standardize preference implementations with Dagger/Hilt and `CoroutineDispatchers`. Signed-off-by: James Rich <[email protected]>
- Replace `SharedPreferences` with `DataStore<Preferences>` across all preference implementation classes, including `AnalyticsPrefs`, `MeshPrefs`, `UiPrefs`, `MapPrefs`, and `RadioPrefs`. - Remove custom `SharedPreferences` property delegates (`PrefDelegate`, `DoublePrefDelegate`, etc.) and the `preferenceFlow` helper in favor of `DataStore`'s native Flow support. - Update `PrefsModule` and `GoogleMapsModule` to provide `DataStore` instances using `SharedPreferencesMigration` to ensure data persistence for existing users. - Refactor `GoogleMapsPrefs` to expose settings as `StateFlow` properties and use explicit setter methods for updates. - Update `MapViewModel` to consume preference changes via `StateFlow` and use asynchronous setters. - Relocate `DataStoreModule` from `core:datastore` to `core:data`. - Update `FilterPrefsTest` to use `PreferenceDataStoreFactory` and `runTest` for verifying preference logic. - Update module dependencies to include `androidx.datastore` and remove obsolete references to `core:prefs` where applicable. Signed-off-by: James Rich <[email protected]>
- Standardize `DataStore` preference implementations by compacting `StateFlow` declarations and applying consistent formatting to `edit` blocks across all `PrefsImpl` classes.
- Update copyright headers to include 2026 in core datastore and model files.
- Improve unit test reliability in `MessageFilterImplTest` and `MapViewModelTest` by using `MutableStateFlow` to mock reactive preference properties.
- Perform general code style cleanup, including adding trailing commas, adjusting vertical whitespace in interfaces/modules, and removing redundant safe calls in `MetricsViewModel`.
- Add `@Suppress("TooManyFunctions")` to the `MeshLogRepository` interface.
- Ensure consistent use of `SharingStarted.Eagerly` for preference-backed `StateFlow`s.
Signed-off-by: James Rich <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR refactors the app’s settings infrastructure to support Kotlin Multiplatform by moving preference interfaces into :core:repository, migrating Android implementations from SharedPreferences to DataStore<Preferences> (with SharedPreferencesMigration), and converting many preference reads/writes to reactive StateFlow patterns across features and core modules.
Changes:
- Migrates preference APIs from synchronous getters/setters to
StateFlow+ explicit setter methods and updates call sites. - Introduces/relocates KMP-friendly repository interfaces (e.g.,
AppPreferences,MeshLogRepository) and updates DI bindings. - Converts
:core:datastoreto KMP using Okio (OkioSerializer/OkioStorage) and adds new proto DataSources.
Reviewed changes
Copilot reviewed 109 out of 113 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| feature/settings/src/test/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModelTest.kt | Updates preference interface imports to repository module. |
| feature/settings/src/test/kotlin/org/meshtastic/feature/settings/filter/FilterSettingsViewModelTest.kt | Adjusts tests for StateFlow-based prefs and setter methods. |
| feature/settings/src/test/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModelTest.kt | Updates mocks/verifications for flow-based MeshLog prefs. |
| feature/settings/src/test/kotlin/org/meshtastic/feature/settings/SettingsViewModelTest.kt | Updates imports for moved DatabaseManager/prefs interfaces. |
| feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/MQTTConfigItemList.kt | Uses reactive consent preference via StateFlow. |
| feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt | Switches analytics/homoglyph flows to repository prefs StateFlows. |
| feature/settings/src/main/kotlin/org/meshtastic/feature/settings/filter/FilterSettingsViewModel.kt | Migrates filter prefs usage to flow + setter methods. |
| feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt | Migrates MeshLog prefs usage to flow + setter methods. |
| feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt | Migrates MeshLog prefs reads to flow-based values. |
| feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt | Updates repository dependency and telemetry time filtering logic. |
| feature/node/src/main/kotlin/org/meshtastic/feature/node/domain/usecase/GetNodeDetailsUseCase.kt | Updates MeshLogRepository import to new shared interface. |
| feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt | Migrates UI/emoji/homoglyph prefs usage to flow-based prefs. |
| feature/map/src/testGoogle/kotlin/org/meshtastic/feature/map/MapViewModelTest.kt | Updates map prefs mocks to provide StateFlow values. |
| feature/map/src/main/kotlin/org/meshtastic/feature/map/node/NodeMapViewModel.kt | Updates MapPrefs access to StateFlow value. |
| feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt | Migrates map prefs access to flow-based prefs + setter methods. |
| feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt | Updates Google map prefs to flow-based getters + setter methods. |
| feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewModel.kt | Updates MapPrefs map style to StateFlow and setter method. |
| feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt | Updates RadioPrefs to flow-based devAddr. |
| feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateManager.kt | Updates RadioPrefs access to devAddr.value. |
| core/ui/src/main/kotlin/org/meshtastic/core/ui/emoji/EmojiPickerViewModel.kt | Migrates emoji frequency preference to StateFlow + setter. |
| core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCase.kt | Reads homoglyph encoding enabled from StateFlow. |
| core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/MeshLogRepository.kt | Adds shared (KMP) MeshLogRepository interface. |
| core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/HomoglyphPrefs.kt | Removes obsolete non-reactive homoglyph prefs interface file. |
| core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/AppPreferences.kt | Adds consolidated reactive preference interfaces for KMP. |
| core/repository/build.gradle.kts | Adds database dependency needed by repository module. |
| core/prefs/src/test/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsTest.kt | Migrates tests to DataStore-backed FilterPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/ui/UiPrefsImpl.kt | Adds DataStore-backed UiPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/ui/UiPrefs.kt | Removes SharedPreferences-based UiPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/radio/RadioPrefsImpl.kt | Adds DataStore-backed RadioPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/radio/RadioPrefs.kt | Removes SharedPreferences-based RadioPrefs API/impl and extensions. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/meshlog/MeshLogPrefsImpl.kt | Adds DataStore-backed MeshLogPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/meshlog/MeshLogPrefs.kt | Removes SharedPreferences-based MeshLogPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/mesh/MeshPrefsImpl.kt | Adds DataStore-backed MeshPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/mesh/MeshPrefs.kt | Removes SharedPreferences-based MeshPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapTileProviderPrefsImpl.kt | Adds DataStore-backed tile provider prefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapTileProviderPrefs.kt | Removes SharedPreferences-based tile provider prefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapPrefsImpl.kt | Adds DataStore-backed MapPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapPrefs.kt | Removes SharedPreferences-based MapPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapConsentPrefsImpl.kt | Adds DataStore-backed MapConsentPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/map/MapConsentPrefs.kt | Removes SharedPreferences-based MapConsentPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/homoglyph/HomoglyphPrefsImpl.kt | Adds DataStore-backed HomoglyphPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/homoglyph/HomoglyphPrefs.kt | Removes SharedPreferences-based HomoglyphPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/filter/FilterPrefsImpl.kt | Adds DataStore-backed FilterPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/filter/FilterPrefs.kt | Removes SharedPreferences-based FilterPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/emoji/CustomEmojiPrefsImpl.kt | Adds DataStore-backed CustomEmojiPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/emoji/CustomEmojiPrefs.kt | Removes SharedPreferences-based CustomEmojiPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/di/PrefsModule.kt | Replaces SharedPreferences providers with DataStore providers + migrations. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefsImpl.kt | Adds DataStore-backed AnalyticsPrefs implementation. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefs.kt | Removes SharedPreferences-based AnalyticsPrefs API/impl. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/StringSetPrefDelegate.kt | Removes SharedPreferences delegate helper. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/PrefDelegate.kt | Removes SharedPreferences delegate helper. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/NullableStringPrefDelegate.kt | Removes SharedPreferences delegate helper. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/FloatPrefDelegate.kt | Removes SharedPreferences delegate helper. |
| core/prefs/src/main/kotlin/org/meshtastic/core/prefs/DoublePrefDelegate.kt | Removes SharedPreferences delegate helper. |
| core/prefs/src/google/kotlin/org/meshtastic/core/prefs/map/GoogleMapsPrefs.kt | Migrates GoogleMapsPrefs to DataStore + StateFlow API. |
| core/prefs/src/google/kotlin/org/meshtastic/core/prefs/di/GoogleMapsModule.kt | Provides GoogleMaps DataStore + migration via Hilt. |
| core/prefs/build.gradle.kts | Adds DataStore/coroutines deps for new pref implementations and tests. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCaseTest.kt | Updates test to use flow-based HomoglyphPrefs API. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCaseTest.kt | Updates test to use flow-based AnalyticsPrefs API. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCaseTest.kt | Updates UiPrefs import to repository module. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCaseTest.kt | Updates MeshLog prefs/repo imports + flow access. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCaseTest.kt | Updates DatabaseManager import to new common location. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCaseTest.kt | Updates RadioPrefs mocking to provide StateFlow. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCaseTest.kt | Updates MeshLogRepository import to shared interface. |
| core/domain/src/test/kotlin/org/meshtastic/core/domain/usecase/SendMessageUseCaseTest.kt | Updates homoglyph enabled mocking to use StateFlow. |
| core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleHomoglyphEncodingUseCase.kt | Migrates toggle logic to StateFlow + setter. |
| core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ToggleAnalyticsUseCase.kt | Migrates toggle logic to StateFlow + setter. |
| core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetProvideLocationUseCase.kt | Updates UiPrefs import to repository module. |
| core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetMeshLogSettingsUseCase.kt | Migrates MeshLog setting updates to new prefs API. |
| core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/SetDatabaseCacheLimitUseCase.kt | Updates DatabaseManager import to core.common.database. |
| core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/IsOtaCapableUseCase.kt | Updates RadioPrefs imports/extensions to repository module. |
| core/domain/src/main/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt | Updates MeshLogRepository import to shared interface. |
| core/domain/build.gradle.kts | Removes core:prefs/core:data deps from domain to support decoupling. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/serializer/ModuleConfigSerializer.kt | Converts proto serializer to OkioSerializer for KMP. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/serializer/LocalStatsSerializer.kt | Converts proto serializer to OkioSerializer for KMP. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/serializer/LocalConfigSerializer.kt | Converts proto serializer to OkioSerializer for KMP. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/serializer/ChannelSetSerializer.kt | Converts proto serializer to OkioSerializer for KMP. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/model/RecentAddress.kt | Updates header; minor formatting cleanup. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/UiPreferencesDataSource.kt | Exposes keys as public constants (visibility change). |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/RecentAddressesDataSource.kt | Updates header; minor formatting cleanup. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/ModuleConfigDataSource.kt | Adds DataSource wrapper for LocalModuleConfig DataStore. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/LocalStatsDataSource.kt | Adds DataSource wrapper for LocalStats DataStore. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/LocalConfigDataSource.kt | Adds DataSource wrapper for LocalConfig DataStore. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/ChannelSetDataSource.kt | Adds DataSource wrapper for ChannelSet DataStore. |
| core/datastore/src/commonMain/kotlin/org/meshtastic/core/datastore/BootloaderWarningDataSource.kt | Updates header; minor formatting cleanup. |
| core/datastore/build.gradle.kts | Converts module to KMP and adds Okio/Hilt-related wiring. |
| core/database/src/androidMain/kotlin/org/meshtastic/core/database/DatabaseManager.kt | Updates shared DatabaseManager import alias to new package. |
| core/database/build.gradle.kts | Removes repository dependency from database module to break coupling. |
| core/data/src/test/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryTest.kt | Updates to use MeshLogRepositoryImpl name and new prefs import. |
| core/data/src/test/kotlin/org/meshtastic/core/data/manager/PacketHandlerImplTest.kt | Updates MeshLogRepository import to shared interface. |
| core/data/src/test/kotlin/org/meshtastic/core/data/manager/MessageFilterImplTest.kt | Updates filter prefs mocking for flow-based prefs. |
| core/data/src/test/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImplTest.kt | Updates UiPrefs import to repository module. |
| core/data/src/main/kotlin/org/meshtastic/core/data/repository/MeshLogRepositoryImpl.kt | Implements new shared MeshLogRepository interface. |
| core/data/src/main/kotlin/org/meshtastic/core/data/manager/PacketHandlerImpl.kt | Updates MeshLogRepository import to shared interface. |
| core/data/src/main/kotlin/org/meshtastic/core/data/manager/MessageFilterImpl.kt | Updates FilterPrefs access to use StateFlow.value. |
| core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshMessageProcessorImpl.kt | Updates MeshLogRepository import to shared interface. |
| core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshConnectionManagerImpl.kt | Updates UiPrefs import to repository module. |
| core/data/src/main/kotlin/org/meshtastic/core/data/manager/MeshActionHandlerImpl.kt | Updates MeshPrefs/DatabaseManager imports and flow-based reads. |
| core/data/src/main/kotlin/org/meshtastic/core/data/manager/HistoryManagerImpl.kt | Updates MeshPrefs reads to use StateFlow.value. |
| core/data/src/main/kotlin/org/meshtastic/core/data/di/RepositoryModule.kt | Binds MeshLogRepository interface to MeshLogRepositoryImpl. |
| core/data/src/main/kotlin/org/meshtastic/core/data/di/DatabaseModule.kt | Rebinds DatabaseManager to new core.common.database interface. |
| core/data/src/main/kotlin/org/meshtastic/core/data/di/DataStoreModule.kt | Moves DataStore module and switches proto stores to OkioStorage. |
| core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt | Updates MapTileProviderPrefs usage to flow + setter API. |
| core/data/build.gradle.kts | Adds datastore deps needed by refactored DataStore module usage. |
| core/common/src/commonMain/kotlin/org/meshtastic/core/common/database/DatabaseManager.kt | Moves DatabaseManager interface into core.common.database package. |
| core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt | Updates analytics preference listening to StateFlow. |
| core/analytics/build.gradle.kts | Adds repository dependency to match new prefs interface location. |
| app/src/main/java/com/geeksville/mesh/worker/MeshLogCleanupWorker.kt | Migrates MeshLog prefs to flow-based reads and repository imports. |
| app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsViewModel.kt | Migrates UiPrefs usage to flow-based reads and setter methods. |
| app/src/main/java/com/geeksville/mesh/repository/radio/AndroidRadioInterfaceService.kt | Migrates RadioPrefs usage to flow-based reads and setter method. |
| app/src/main/java/com/geeksville/mesh/model/UIViewModel.kt | Updates MeshLogRepository import to shared interface. |
| app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt | Updates MeshPrefs/MeshLogPrefs imports and flow-based access. |
| app/src/androidTest/java/com/geeksville/mesh/filter/MessageFilterIntegrationTest.kt | Updates filter prefs to setter methods and repository import. |
You can also share your feedback on Copilot code review. Take the survey.
core/prefs/src/main/kotlin/org/meshtastic/core/prefs/analytics/AnalyticsPrefsImpl.kt
Outdated
Show resolved
Hide resolved
…checks - Update `AppPreferences` interface and `AnalyticsPrefsImpl` to expose `installId` as a `StateFlow<String>`, facilitating reactive and non-blocking access. - Remove `runBlocking` call in `AnalyticsPrefsImpl` and move UUID generation logic to an asynchronous `init` block. - Simplify logic in `MetricsViewModel` by removing redundant null-coalescing operators (`?: 0`) on properties that are now non-nullable. Signed-off-by: James Rich <[email protected]>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4731 +/- ##
=====================================
Coverage 0.00% 0.00%
=====================================
Files 3 3
Lines 231 231
Branches 34 34
=====================================
Misses 231 231 ☔ View full report in Codecov by Sentry. |
Key Changes