mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor: migrate from Hilt to Koin and expand KMP common modules (#4746)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
a5390a80e7
commit
875cf1cff2
440 changed files with 3738 additions and 3508 deletions
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 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.core.okio.OkioStorage
|
||||
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 kotlinx.coroutines.CoroutineScope
|
||||
import okio.FileSystem
|
||||
import okio.Path.Companion.toOkioPath
|
||||
import org.koin.core.annotation.Module
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
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
|
||||
|
||||
private const val USER_PREFERENCES_NAME = "user_preferences"
|
||||
|
||||
@Module
|
||||
class PreferencesDataStoreModule {
|
||||
@Single
|
||||
@Named("CorePreferencesDataStore")
|
||||
fun providePreferencesDataStore(
|
||||
context: Context,
|
||||
@Named("DataStoreScope") scope: CoroutineScope,
|
||||
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
|
||||
migrations =
|
||||
listOf(
|
||||
SharedPreferencesMigration(context = context, sharedPreferencesName = USER_PREFERENCES_NAME),
|
||||
SharedPreferencesMigration(
|
||||
context = context,
|
||||
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 = { context.preferencesDataStoreFile(USER_PREFERENCES_NAME) },
|
||||
)
|
||||
}
|
||||
|
||||
@Module
|
||||
class LocalConfigDataStoreModule {
|
||||
@Single
|
||||
@Named("CoreLocalConfigDataStore")
|
||||
fun provideLocalConfigDataStore(
|
||||
context: Context,
|
||||
@Named("DataStoreScope") scope: CoroutineScope,
|
||||
): DataStore<LocalConfig> = DataStoreFactory.create(
|
||||
storage =
|
||||
OkioStorage(
|
||||
fileSystem = FileSystem.SYSTEM,
|
||||
serializer = LocalConfigSerializer,
|
||||
producePath = { context.dataStoreFile("local_config.pb").toOkioPath() },
|
||||
),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalConfig() }),
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
||||
@Module
|
||||
class ModuleConfigDataStoreModule {
|
||||
@Single
|
||||
@Named("CoreModuleConfigDataStore")
|
||||
fun provideModuleConfigDataStore(
|
||||
context: Context,
|
||||
@Named("DataStoreScope") scope: CoroutineScope,
|
||||
): DataStore<LocalModuleConfig> = DataStoreFactory.create(
|
||||
storage =
|
||||
OkioStorage(
|
||||
fileSystem = FileSystem.SYSTEM,
|
||||
serializer = ModuleConfigSerializer,
|
||||
producePath = { context.dataStoreFile("module_config.pb").toOkioPath() },
|
||||
),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalModuleConfig() }),
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
||||
@Module
|
||||
class ChannelSetDataStoreModule {
|
||||
@Single
|
||||
@Named("CoreChannelSetDataStore")
|
||||
fun provideChannelSetDataStore(
|
||||
context: Context,
|
||||
@Named("DataStoreScope") scope: CoroutineScope,
|
||||
): DataStore<ChannelSet> = DataStoreFactory.create(
|
||||
storage =
|
||||
OkioStorage(
|
||||
fileSystem = FileSystem.SYSTEM,
|
||||
serializer = ChannelSetSerializer,
|
||||
producePath = { context.dataStoreFile("channel_set.pb").toOkioPath() },
|
||||
),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { ChannelSet() }),
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
||||
@Module
|
||||
class LocalStatsDataStoreModule {
|
||||
@Single
|
||||
@Named("CoreLocalStatsDataStore")
|
||||
fun provideLocalStatsDataStore(
|
||||
context: Context,
|
||||
@Named("DataStoreScope") scope: CoroutineScope,
|
||||
): DataStore<LocalStats> = DataStoreFactory.create(
|
||||
storage =
|
||||
OkioStorage(
|
||||
fileSystem = FileSystem.SYSTEM,
|
||||
serializer = LocalStatsSerializer,
|
||||
producePath = { context.dataStoreFile("local_stats.pb").toOkioPath() },
|
||||
),
|
||||
corruptionHandler = ReplaceFileCorruptionHandler(produceNewData = { LocalStats() }),
|
||||
scope = scope,
|
||||
)
|
||||
}
|
||||
|
||||
@Module(
|
||||
includes =
|
||||
[
|
||||
PreferencesDataStoreModule::class,
|
||||
LocalConfigDataStoreModule::class,
|
||||
ModuleConfigDataStoreModule::class,
|
||||
ChannelSetDataStoreModule::class,
|
||||
LocalStatsDataStoreModule::class,
|
||||
],
|
||||
)
|
||||
class CoreDatastoreAndroidModule
|
||||
|
|
@ -25,11 +25,11 @@ import kotlinx.coroutines.flow.first
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.Json
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@Singleton
|
||||
class BootloaderWarningDataSource @Inject constructor(private val dataStore: DataStore<Preferences>) {
|
||||
@Single
|
||||
class BootloaderWarningDataSource(@Named("CorePreferencesDataStore") private val dataStore: DataStore<Preferences>) {
|
||||
|
||||
private object PreferencesKeys {
|
||||
val DISMISSED_BOOTLOADER_ADDRESSES = stringPreferencesKey("dismissed-bootloader-addresses")
|
||||
|
|
|
|||
|
|
@ -21,16 +21,16 @@ import co.touchlab.kermit.Logger
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import okio.IOException
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
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
|
||||
|
||||
/** Class that handles saving and retrieving [ChannelSet] data. */
|
||||
@Singleton
|
||||
class ChannelSetDataSource @Inject constructor(private val channelSetStore: DataStore<ChannelSet>) {
|
||||
@Single
|
||||
class ChannelSetDataSource(@Named("CoreChannelSetDataStore") private val channelSetStore: DataStore<ChannelSet>) {
|
||||
val channelSetFlow: Flow<ChannelSet> =
|
||||
channelSetStore.data.catch { exception ->
|
||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ import co.touchlab.kermit.Logger
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import okio.IOException
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.proto.Config
|
||||
import org.meshtastic.proto.LocalConfig
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/** Class that handles saving and retrieving [LocalConfig] data. */
|
||||
@Singleton
|
||||
class LocalConfigDataSource @Inject constructor(private val localConfigStore: DataStore<LocalConfig>) {
|
||||
@Single
|
||||
class LocalConfigDataSource(@Named("CoreLocalConfigDataStore") private val localConfigStore: DataStore<LocalConfig>) {
|
||||
val localConfigFlow: Flow<LocalConfig> =
|
||||
localConfigStore.data.catch { exception ->
|
||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ import co.touchlab.kermit.Logger
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import okio.IOException
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.proto.LocalStats
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/** Class that handles saving and retrieving [LocalStats] data. */
|
||||
@Singleton
|
||||
class LocalStatsDataSource @Inject constructor(private val localStatsStore: DataStore<LocalStats>) {
|
||||
@Single
|
||||
class LocalStatsDataSource(@Named("CoreLocalStatsDataStore") private val localStatsStore: DataStore<LocalStats>) {
|
||||
val localStatsFlow: Flow<LocalStats> =
|
||||
localStatsStore.data.catch { exception ->
|
||||
if (exception is IOException) {
|
||||
|
|
|
|||
|
|
@ -21,14 +21,16 @@ import co.touchlab.kermit.Logger
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import okio.IOException
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.proto.LocalModuleConfig
|
||||
import org.meshtastic.proto.ModuleConfig
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/** Class that handles saving and retrieving [LocalModuleConfig] data. */
|
||||
@Singleton
|
||||
class ModuleConfigDataSource @Inject constructor(private val moduleConfigStore: DataStore<LocalModuleConfig>) {
|
||||
@Single
|
||||
class ModuleConfigDataSource(
|
||||
@Named("CoreModuleConfigDataStore") private val moduleConfigStore: DataStore<LocalModuleConfig>,
|
||||
) {
|
||||
val moduleConfigFlow: Flow<LocalModuleConfig> =
|
||||
moduleConfigStore.data.catch { exception ->
|
||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ import kotlinx.serialization.SerializationException
|
|||
import kotlinx.serialization.json.Json
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RecentAddressesDataSource @Inject constructor(private val dataStore: DataStore<Preferences>) {
|
||||
@Single
|
||||
class RecentAddressesDataSource(@Named("CorePreferencesDataStore") private val dataStore: DataStore<Preferences>) {
|
||||
private object PreferencesKeys {
|
||||
val RECENT_IP_ADDRESSES = stringPreferencesKey("recent-ip-addresses")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
const val KEY_APP_INTRO_COMPLETED = "app_intro_completed"
|
||||
const val KEY_THEME = "theme"
|
||||
|
|
@ -43,8 +43,8 @@ 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>) {
|
||||
@Single
|
||||
class UiPreferencesDataSource(@Named("CorePreferencesDataStore") private val dataStore: DataStore<Preferences>) {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 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 kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import org.koin.core.annotation.ComponentScan
|
||||
import org.koin.core.annotation.Module
|
||||
import org.koin.core.annotation.Named
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@Module
|
||||
@ComponentScan("org.meshtastic.core.datastore")
|
||||
class CoreDatastoreModule {
|
||||
@Single
|
||||
@Named("DataStoreScope")
|
||||
fun provideDataStoreScope(): CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue