Modularize prefs classes (#3171)

This commit is contained in:
Phil Oliver 2025-09-23 05:51:03 -04:00 committed by GitHub
parent 53fdda3a9c
commit b98e533123
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 290 additions and 234 deletions

View file

@ -41,7 +41,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.prefs.UiPrefs
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.MainScreen
import com.geeksville.mesh.ui.common.theme.AppTheme
@ -50,6 +49,7 @@ import com.geeksville.mesh.ui.intro.AppIntroductionScreen
import com.geeksville.mesh.ui.sharing.toSharedContact
import dagger.hilt.android.AndroidEntryPoint
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.prefs.ui.UiPrefs
import javax.inject.Inject
@AndroidEntryPoint

View file

@ -1,36 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
import java.util.UUID
interface AnalyticsPrefs {
var analyticsAllowed: Boolean
val installId: String
}
// Having an additional app prefs store is maintaining the existing behavior.
class AnalyticsPrefsImpl(analyticsPrefs: SharedPreferences, appPrefs: SharedPreferences) : AnalyticsPrefs {
override var analyticsAllowed: Boolean by PrefDelegate(analyticsPrefs, "allowed", true)
private var _installId: String? by NullableStringPrefDelegate(appPrefs, "appPrefs_install_id", null)
override val installId: String
get() = _installId ?: UUID.randomUUID().toString().also { _installId = it }
}

View file

@ -1,28 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
interface CustomEmojiPrefs {
var customEmojiFrequency: String?
}
class CustomEmojiPrefsImpl(prefs: SharedPreferences) : CustomEmojiPrefs {
override var customEmojiFrequency: String? by NullableStringPrefDelegate(prefs, "pref_key_custom_emoji_freq", null)
}

View file

@ -1,35 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
interface MapConsentPrefs {
fun shouldReportLocation(nodeNum: Int?): Boolean
fun setShouldReportLocation(nodeNum: Int?, value: Boolean)
}
class MapConsentPrefsImpl(private val prefs: SharedPreferences) : MapConsentPrefs {
override fun shouldReportLocation(nodeNum: Int?) = prefs.getBoolean(nodeNum.toString(), false)
override fun setShouldReportLocation(nodeNum: Int?, value: Boolean) {
prefs.edit { putBoolean(nodeNum.toString(), value) }
}
}

View file

@ -1,35 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
/** Interface for general map prefs. For Google-specific prefs, see GoogleMapsPrefs. */
interface MapPrefs {
var mapStyle: Int
var showOnlyFavorites: Boolean
var showWaypointsOnMap: Boolean
var showPrecisionCircleOnMap: Boolean
}
class MapPrefsImpl(prefs: SharedPreferences) : MapPrefs {
override var mapStyle: Int by PrefDelegate(prefs, "map_style_id", 0)
override var showOnlyFavorites: Boolean by PrefDelegate(prefs, "show_only_favorites", false)
override var showWaypointsOnMap: Boolean by PrefDelegate(prefs, "show_waypoints", true)
override var showPrecisionCircleOnMap: Boolean by PrefDelegate(prefs, "show_precision_circle", true)
}

View file

@ -1,28 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
interface MapTileProviderPrefs {
var customTileProviders: String?
}
class MapTileProviderPrefsImpl(prefs: SharedPreferences) : MapTileProviderPrefs {
override var customTileProviders: String? by NullableStringPrefDelegate(prefs, "custom_tile_providers", null)
}

View file

@ -1,42 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
interface MeshPrefs {
var deviceAddress: String?
fun shouldProvideNodeLocation(nodeNum: Int?): Boolean
fun setShouldProvideNodeLocation(nodeNum: Int?, value: Boolean)
}
class MeshPrefsImpl(private val prefs: SharedPreferences) : MeshPrefs {
override var deviceAddress: String? by NullableStringPrefDelegate(prefs, "device_address", null)
override fun shouldProvideNodeLocation(nodeNum: Int?): Boolean =
prefs.getBoolean(provideLocationKey(nodeNum), false)
override fun setShouldProvideNodeLocation(nodeNum: Int?, value: Boolean) {
prefs.edit { putBoolean(provideLocationKey(nodeNum), value) }
}
private fun provideLocationKey(nodeNum: Int?) = "provide-location-$nodeNum"
}

View file

@ -1,48 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* A [ReadWriteProperty] delegate that provides concise, type-safe access to [SharedPreferences] for nullable strings.
*
* @param prefs The [SharedPreferences] instance to back the property.
* @param key The key used to store and retrieve the value.
* @param defaultValue The default value to return if no value is found.
*/
class NullableStringPrefDelegate(
private val prefs: SharedPreferences,
private val key: String,
private val defaultValue: String?,
) : ReadWriteProperty<Any?, String?> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String? = prefs.getString(key, defaultValue)
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) {
prefs.edit {
when (value) {
null -> remove(key)
else -> putString(key, value)
}
}
}
}

View file

@ -1,58 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* A generic [ReadWriteProperty] delegate that provides concise, type-safe access to [SharedPreferences].
*
* @param prefs The [SharedPreferences] instance to back the property.
* @param key The key used to store and retrieve the value.
* @param defaultValue The default value to return if no value is found.
* @throws IllegalArgumentException if the type is not supported.
*/
class PrefDelegate<T>(private val prefs: SharedPreferences, private val key: String, private val defaultValue: T) :
ReadWriteProperty<Any?, T> {
@Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Any?, property: KProperty<*>): T = when (defaultValue) {
is String -> (prefs.getString(key, defaultValue) ?: defaultValue) as T
is Int -> prefs.getInt(key, defaultValue) as T
is Boolean -> prefs.getBoolean(key, defaultValue) as T
is Float -> prefs.getFloat(key, defaultValue) as T
is Long -> prefs.getLong(key, defaultValue) as T
else -> error("Unsupported type for key '$key': $defaultValue")
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
prefs.edit {
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Boolean -> putBoolean(key, value)
is Float -> putFloat(key, value)
is Long -> putLong(key, value)
else -> error("Unsupported type for key '$key': $value")
}
}
}
}

View file

@ -1,170 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.Context
import android.content.SharedPreferences
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Qualifier
import javax.inject.Singleton
// These pref store qualifiers are private to prevent prefs stores from being injected directly.
// Consuming code should always inject one of the prefs repositories.
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class AnalyticsSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class AppSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class CustomEmojiSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class MapSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class MapConsentSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class MapTileProviderSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class MeshSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class RadioSharedPreferences
@Qualifier
@Retention(AnnotationRetention.BINARY)
private annotation class UiSharedPreferences
@Suppress("TooManyFunctions")
@InstallIn(SingletonComponent::class)
@Module
object PrefsModule {
@Provides
@Singleton
@AnalyticsSharedPreferences
fun provideAnalyticsSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("analytics-prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
@AppSharedPreferences
fun provideAppSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
@CustomEmojiSharedPreferences
fun provideCustomEmojiSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("org.geeksville.emoji.prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
@MapSharedPreferences
fun provideMapSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("map_prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
@MapConsentSharedPreferences
fun provideMapConsentSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("map_consent_preferences", Context.MODE_PRIVATE)
@Provides
@Singleton
@MapTileProviderSharedPreferences
fun provideMapTileProviderSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("map_tile_provider_prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
@MeshSharedPreferences
fun provideMeshSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("mesh-prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
@RadioSharedPreferences
fun provideRadioSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("radio-prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
@UiSharedPreferences
fun provideUiSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
@Provides
@Singleton
fun provideAnalyticsPrefs(
@AnalyticsSharedPreferences analyticsPreferences: SharedPreferences,
@AppSharedPreferences appPreferences: SharedPreferences,
): AnalyticsPrefs = AnalyticsPrefsImpl(analyticsPreferences, appPreferences)
@Provides
@Singleton
fun provideCustomEmojiPrefs(@CustomEmojiSharedPreferences sharedPreferences: SharedPreferences): CustomEmojiPrefs =
CustomEmojiPrefsImpl(sharedPreferences)
@Provides
@Singleton
fun provideMapPrefs(@MapSharedPreferences sharedPreferences: SharedPreferences): MapPrefs =
MapPrefsImpl(sharedPreferences)
@Provides
@Singleton
fun provideMapConsentPrefs(@MapConsentSharedPreferences sharedPreferences: SharedPreferences): MapConsentPrefs =
MapConsentPrefsImpl(sharedPreferences)
@Provides
@Singleton
fun provideMapTileProviderPrefs(
@MapTileProviderSharedPreferences sharedPreferences: SharedPreferences,
): MapTileProviderPrefs = MapTileProviderPrefsImpl(sharedPreferences)
@Provides
@Singleton
fun provideMeshPrefs(@MeshSharedPreferences sharedPreferences: SharedPreferences): MeshPrefs =
MeshPrefsImpl(sharedPreferences)
@Provides
@Singleton
fun provideRadioPrefs(@RadioSharedPreferences sharedPreferences: SharedPreferences): RadioPrefs =
RadioPrefsImpl(sharedPreferences)
@Provides
@Singleton
fun provideUiPrefs(@UiSharedPreferences sharedPreferences: SharedPreferences): UiPrefs =
UiPrefsImpl(sharedPreferences)
}

View file

@ -1,28 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
interface RadioPrefs {
var devAddr: String?
}
class RadioPrefsImpl(prefs: SharedPreferences) : RadioPrefs {
override var devAddr: String? by NullableStringPrefDelegate(prefs, "devAddr2", null)
}

View file

@ -1,35 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class StringSetPrefDelegate(
private val prefs: SharedPreferences,
private val key: String,
private val defaultValue: Set<String>,
) : ReadWriteProperty<Any?, Set<String>> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Set<String> =
prefs.getStringSet(key, defaultValue) ?: emptySet()
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Set<String>) =
prefs.edit { putStringSet(key, value) }
}

View file

@ -1,104 +0,0 @@
/*
* 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/>.
*/
package com.geeksville.mesh.android.prefs
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.edit
import com.geeksville.mesh.model.NodeSortOption
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import java.util.concurrent.ConcurrentHashMap
interface UiPrefs {
var theme: Int
val themeFlow: StateFlow<Int>
var appIntroCompleted: Boolean
val appIntroCompletedFlow: StateFlow<Boolean>
var hasShownNotPairedWarning: Boolean
var nodeSortOption: Int
var includeUnknown: Boolean
var showDetails: Boolean
var onlyOnline: Boolean
var onlyDirect: Boolean
var showIgnored: Boolean
var showQuickChat: Boolean
fun shouldProvideNodeLocation(nodeNum: Int): StateFlow<Boolean>
fun setShouldProvideNodeLocation(nodeNum: Int, value: Boolean)
}
const val KEY_THEME = "theme"
const val KEY_APP_INTRO_COMPLETED = "app_intro_completed"
class UiPrefsImpl(private val prefs: SharedPreferences) : UiPrefs {
override var theme: Int by PrefDelegate(prefs, KEY_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
private var _themeFlow = MutableStateFlow(theme)
override val themeFlow = _themeFlow.asStateFlow()
override var appIntroCompleted: Boolean by PrefDelegate(prefs, KEY_APP_INTRO_COMPLETED, false)
private var _appIntroCompletedFlow = MutableStateFlow(appIntroCompleted)
override val appIntroCompletedFlow = _appIntroCompletedFlow.asStateFlow()
// Maps nodeNum to a flow for the for the "provide-location-nodeNum" pref
private val provideNodeLocationFlows = ConcurrentHashMap<Int, MutableStateFlow<Boolean>>()
private val sharedPreferencesListener =
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
when (key) {
KEY_THEME -> _themeFlow.update { theme }
KEY_APP_INTRO_COMPLETED -> _appIntroCompletedFlow.update { appIntroCompleted }
// Check if the changed key is one of our node location keys
else ->
provideNodeLocationFlows.keys.forEach { nodeNum ->
if (key == provideLocationKey(nodeNum)) {
val newValue = sharedPreferences.getBoolean(key, false)
provideNodeLocationFlows[nodeNum]?.tryEmit(newValue)
}
}
}
}
init {
prefs.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
}
override var hasShownNotPairedWarning: Boolean by PrefDelegate(prefs, "has_shown_not_paired_warning", false)
override var nodeSortOption: Int by PrefDelegate(prefs, "node-sort-option", NodeSortOption.VIA_FAVORITE.ordinal)
override var includeUnknown: Boolean by PrefDelegate(prefs, "include-unknown", false)
override var showDetails: Boolean by PrefDelegate(prefs, "show-details", false)
override var onlyOnline: Boolean by PrefDelegate(prefs, "only-online", false)
override var onlyDirect: Boolean by PrefDelegate(prefs, "only-direct", false)
override var showIgnored: Boolean by PrefDelegate(prefs, "show-ignored", false)
override var showQuickChat: Boolean by PrefDelegate(prefs, "show-quick-chat", false)
override fun shouldProvideNodeLocation(nodeNum: Int): StateFlow<Boolean> = provideNodeLocationFlows
.getOrPut(nodeNum) { MutableStateFlow(prefs.getBoolean(provideLocationKey(nodeNum), false)) }
.asStateFlow()
override fun setShouldProvideNodeLocation(nodeNum: Int, value: Boolean) {
prefs.edit { putBoolean(provideLocationKey(nodeNum), value) }
provideNodeLocationFlows[nodeNum]?.tryEmit(value)
}
private fun provideLocationKey(nodeNum: Int) = "provide-location-$nodeNum"
}

View file

@ -36,7 +36,6 @@ import com.geeksville.mesh.Portnums
import com.geeksville.mesh.Portnums.PortNum
import com.geeksville.mesh.TelemetryProtos.Telemetry
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.prefs.MapPrefs
import com.geeksville.mesh.database.MeshLogRepository
import com.geeksville.mesh.database.entity.FirmwareRelease
import com.geeksville.mesh.database.entity.MeshLog
@ -62,6 +61,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.meshtastic.core.model.DeviceHardware
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.core.prefs.map.MapPrefs
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.model.CustomTileSource
import java.io.BufferedWriter

View file

@ -39,7 +39,6 @@ import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.Position
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.prefs.UiPrefs
import com.geeksville.mesh.channel
import com.geeksville.mesh.channelSet
import com.geeksville.mesh.channelSettings
@ -83,6 +82,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.meshtastic.core.model.DeviceHardware
import org.meshtastic.core.prefs.ui.UiPrefs
import org.meshtastic.core.strings.R
import javax.inject.Inject

View file

@ -27,7 +27,6 @@ import com.geeksville.mesh.android.BinaryLogFile
import com.geeksville.mesh.android.BuildUtils
import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.prefs.RadioPrefs
import com.geeksville.mesh.concurrent.handledLaunch
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
import com.geeksville.mesh.repository.network.NetworkRepository
@ -49,6 +48,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.meshtastic.core.prefs.radio.RadioPrefs
import javax.inject.Inject
import javax.inject.Singleton
@ -195,9 +195,7 @@ constructor(
private fun broadcastConnectionChanged(newState: ConnectionState) {
debug("Broadcasting connection state change to $newState")
processLifecycle.coroutineScope.launch(dispatchers.default) {
_connectionState.emit(newState)
}
processLifecycle.coroutineScope.launch(dispatchers.default) { _connectionState.emit(newState) }
}
// Send a packet/command out the radio link, this routine can block if it needs to

View file

@ -58,8 +58,6 @@ import com.geeksville.mesh.analytics.DataPair
import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.hasLocationPermission
import com.geeksville.mesh.android.prefs.MeshPrefs
import com.geeksville.mesh.android.prefs.UiPrefs
import com.geeksville.mesh.concurrent.handledLaunch
import com.geeksville.mesh.copy
import com.geeksville.mesh.database.MeshLogRepository
@ -103,6 +101,8 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.meshtastic.core.model.getFullTracerouteResponse
import org.meshtastic.core.prefs.mesh.MeshPrefs
import org.meshtastic.core.prefs.ui.UiPrefs
import org.meshtastic.core.strings.R
import java.util.Random
import java.util.UUID

View file

@ -18,8 +18,8 @@
package com.geeksville.mesh.ui.common
import androidx.lifecycle.ViewModel
import com.geeksville.mesh.android.prefs.CustomEmojiPrefs
import dagger.hilt.android.lifecycle.HiltViewModel
import org.meshtastic.core.prefs.emoji.CustomEmojiPrefs
import javax.inject.Inject
@HiltViewModel

View file

@ -20,7 +20,6 @@ package com.geeksville.mesh.ui.connections
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
import com.geeksville.mesh.android.prefs.UiPrefs
import com.geeksville.mesh.database.NodeRepository
import com.geeksville.mesh.database.entity.MyNodeEntity
import com.geeksville.mesh.model.Node
@ -32,6 +31,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import org.meshtastic.core.prefs.ui.UiPrefs
import javax.inject.Inject
@HiltViewModel

View file

@ -19,7 +19,6 @@ package com.geeksville.mesh.ui.map
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.geeksville.mesh.android.prefs.MapPrefs
import com.geeksville.mesh.database.NodeRepository
import com.geeksville.mesh.database.PacketRepository
import com.geeksville.mesh.database.entity.Packet
@ -32,6 +31,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import org.meshtastic.core.prefs.map.MapPrefs
@Suppress("TooManyFunctions")
abstract class BaseMapViewModel(

View file

@ -27,7 +27,6 @@ import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.Portnums
import com.geeksville.mesh.Position
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.prefs.UiPrefs
import com.geeksville.mesh.database.MeshLogRepository
import com.geeksville.mesh.database.NodeRepository
import com.geeksville.mesh.database.entity.MyNodeEntity
@ -48,6 +47,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.meshtastic.core.prefs.ui.UiPrefs
import java.io.BufferedWriter
import java.io.FileNotFoundException
import java.io.FileWriter

View file

@ -45,8 +45,6 @@ import com.geeksville.mesh.Position
import com.geeksville.mesh.android.GeeksvilleApplication
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.isAnalyticsAvailable
import com.geeksville.mesh.android.prefs.AnalyticsPrefs
import com.geeksville.mesh.android.prefs.MapConsentPrefs
import com.geeksville.mesh.config
import com.geeksville.mesh.database.entity.MyNodeEntity
import com.geeksville.mesh.deviceProfile
@ -77,6 +75,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.core.prefs.analytics.AnalyticsPrefs
import org.meshtastic.core.prefs.map.MapConsentPrefs
import org.meshtastic.core.strings.R
import java.io.FileOutputStream
import javax.inject.Inject