adding ability to opt in to on device logs, disabled by default

This commit is contained in:
Adam Brown 2022-09-13 21:54:39 +01:00
parent 31318037a7
commit 43e53261b6
18 changed files with 149 additions and 55 deletions

View File

@ -42,10 +42,15 @@ class SmallTalkApplication : Application(), ModuleProvider {
val notificationsModule = featureModules.notificationsModule val notificationsModule = featureModules.notificationsModule
val storeModule = appModule.storeModule.value val storeModule = appModule.storeModule.value
val eventLogStore = storeModule.eventLogStore() val eventLogStore = storeModule.eventLogStore()
val loggingStore = storeModule.loggingStore()
val logger: (String, String) -> Unit = { tag, message -> val logger: (String, String) -> Unit = { tag, message ->
Log.e(tag, message) Log.e(tag, message)
applicationScope.launch { eventLogStore.insert(tag, message) } applicationScope.launch {
if (loggingStore.isEnabled()) {
eventLogStore.insert(tag, message)
}
}
} }
attachAppLogger(logger) attachAppLogger(logger)
_appLogger = logger _appLogger = logger

View File

@ -205,6 +205,7 @@ internal class FeatureModules internal constructor(
deviceMeta, deviceMeta,
coroutineDispatchers, coroutineDispatchers,
coreAndroidModule.themeStore(), coreAndroidModule.themeStore(),
storeModule.value.loggingStore(),
) )
} }
val profileModule by unsafeLazy { ProfileModule(matrixModules.profile, matrixModules.sync, matrixModules.room, trackingModule.errorTracker) } val profileModule by unsafeLazy { ProfileModule(matrixModules.profile, matrixModules.sync, matrixModules.room, trackingModule.errorTracker) }

View File

@ -8,7 +8,13 @@ interface Preferences {
suspend fun remove(key: String) suspend fun remove(key: String)
} }
interface CachedPreferences : Preferences interface CachedPreferences : Preferences {
suspend fun readString(key: String, defaultValue: String): String
}
suspend fun CachedPreferences.readBoolean(key: String, defaultValue: Boolean) = this
.readString(key, defaultValue.toString())
.toBooleanStrict()
suspend fun Preferences.readBoolean(key: String) = this.readString(key)?.toBooleanStrict() suspend fun Preferences.readBoolean(key: String) = this.readString(key)?.toBooleanStrict()
suspend fun Preferences.store(key: String, value: Boolean) = this.store(key, value.toString()) suspend fun Preferences.store(key: String, value: Boolean) = this.store(key, value.toString())

View File

@ -8,12 +8,20 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@Composable @Composable
fun TextRow(title: String, content: String? = null, includeDivider: Boolean = true, onClick: (() -> Unit)? = null, body: @Composable () -> Unit = {}) { fun TextRow(
title: String,
content: String? = null,
includeDivider: Boolean = true,
onClick: (() -> Unit)? = null,
enabled: Boolean = true,
body: @Composable () -> Unit = {}
) {
val modifier = Modifier.padding(horizontal = 24.dp) val modifier = Modifier.padding(horizontal = 24.dp)
Column( Column(
Modifier Modifier
@ -21,14 +29,19 @@ fun TextRow(title: String, content: String? = null, includeDivider: Boolean = tr
.clickable(enabled = onClick != null) { onClick?.invoke() }) { .clickable(enabled = onClick != null) { onClick?.invoke() }) {
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
Column(modifier) { Column(modifier) {
val textModifier = when (enabled) {
true -> Modifier
false -> Modifier.alpha(0.5f)
}
when (content) { when (content) {
null -> { null -> {
Text(text = title, fontSize = 18.sp) Text(text = title, fontSize = 18.sp, modifier = textModifier)
} }
else -> { else -> {
Text(text = title, fontSize = 12.sp) Text(text = title, fontSize = 12.sp, modifier = textModifier)
Spacer(modifier = Modifier.height(2.dp)) Spacer(modifier = Modifier.height(2.dp))
Text(text = content, fontSize = 18.sp) Text(text = content, fontSize = 18.sp, modifier = textModifier)
} }
} }
body() body()
@ -56,6 +69,6 @@ fun IconRow(icon: ImageVector, title: String, onClick: (() -> Unit)? = null) {
} }
@Composable @Composable
fun SettingsTextRow(title: String, subtitle: String?, onClick: (() -> Unit)?) { fun SettingsTextRow(title: String, subtitle: String?, onClick: (() -> Unit)?, enabled: Boolean) {
TextRow(title = title, subtitle, includeDivider = false, onClick) TextRow(title = title, subtitle, includeDivider = false, onClick, enabled = enabled)
} }

View File

@ -6,7 +6,7 @@ class ThemeStore(
private val preferences: CachedPreferences private val preferences: CachedPreferences
) { ) {
suspend fun isMaterialYouEnabled() = preferences.readBoolean(KEY_MATERIAL_YOU_ENABLED) ?: false.also { storeMaterialYouEnabled(false) } suspend fun isMaterialYouEnabled() = preferences.readBoolean(KEY_MATERIAL_YOU_ENABLED, defaultValue = false)
suspend fun storeMaterialYouEnabled(isEnabled: Boolean) { suspend fun storeMaterialYouEnabled(isEnabled: Boolean) {
preferences.store(KEY_MATERIAL_YOU_ENABLED, isEnabled) preferences.store(KEY_MATERIAL_YOU_ENABLED, isEnabled)

View File

@ -6,6 +6,7 @@ import app.dapk.st.core.Preferences
import app.dapk.st.core.extensions.ErrorTracker import app.dapk.st.core.extensions.ErrorTracker
import app.dapk.st.core.extensions.unsafeLazy import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.domain.eventlog.EventLogPersistence import app.dapk.st.domain.eventlog.EventLogPersistence
import app.dapk.st.domain.eventlog.LoggingStore
import app.dapk.st.domain.localecho.LocalEchoPersistence import app.dapk.st.domain.localecho.LocalEchoPersistence
import app.dapk.st.domain.preference.CachingPreferences import app.dapk.st.domain.preference.CachingPreferences
import app.dapk.st.domain.preference.PropertyCache import app.dapk.st.domain.preference.PropertyCache
@ -39,7 +40,7 @@ class StoreModule(
val localEchoStore: LocalEchoStore by unsafeLazy { LocalEchoPersistence(errorTracker, database) } val localEchoStore: LocalEchoStore by unsafeLazy { LocalEchoPersistence(errorTracker, database) }
private val cache = PropertyCache() private val cache = PropertyCache()
val cachingPreferences = CachingPreferences(cache, preferences) val cachingPreferences = CachingPreferences(cache, preferences)
fun pushStore() = PushTokenRegistrarPreferences(preferences) fun pushStore() = PushTokenRegistrarPreferences(preferences)
@ -62,6 +63,8 @@ class StoreModule(
return EventLogPersistence(database, coroutineDispatchers) return EventLogPersistence(database, coroutineDispatchers)
} }
fun loggingStore(): LoggingStore = LoggingStore(cachingPreferences)
fun memberStore(): MemberStore { fun memberStore(): MemberStore {
return MemberPersistence(database, coroutineDispatchers) return MemberPersistence(database, coroutineDispatchers)
} }

View File

@ -0,0 +1,17 @@
package app.dapk.st.domain.eventlog
import app.dapk.st.core.CachedPreferences
import app.dapk.st.core.readBoolean
import app.dapk.st.core.store
private const val KEY_LOGGING_ENABLED = "key_logging_enabled"
class LoggingStore(private val cachedPreferences: CachedPreferences) {
suspend fun isEnabled() = cachedPreferences.readBoolean(KEY_LOGGING_ENABLED, defaultValue = false)
suspend fun setEnabled(isEnabled: Boolean) {
cachedPreferences.store(KEY_LOGGING_ENABLED, isEnabled)
}
}

View File

@ -4,7 +4,6 @@ import app.dapk.st.core.CachedPreferences
import app.dapk.st.core.Preferences import app.dapk.st.core.Preferences
class CachingPreferences(private val cache: PropertyCache, private val preferences: Preferences) : CachedPreferences { class CachingPreferences(private val cache: PropertyCache, private val preferences: Preferences) : CachedPreferences {
override suspend fun store(key: String, value: String) { override suspend fun store(key: String, value: String) {
cache.setValue(key, value) cache.setValue(key, value)
preferences.store(key, value) preferences.store(key, value)
@ -16,6 +15,10 @@ class CachingPreferences(private val cache: PropertyCache, private val preferenc
} }
} }
override suspend fun readString(key: String, defaultValue: String): String {
return readString(key) ?: (defaultValue.also { cache.setValue(key, it) })
}
override suspend fun remove(key: String) { override suspend fun remove(key: String) {
preferences.remove(key) preferences.remove(key)
} }

View File

@ -1,6 +1,10 @@
package app.dapk.st.settings package app.dapk.st.settings
import app.dapk.st.core.* import app.dapk.st.core.BuildMeta
import app.dapk.st.core.DeviceMeta
import app.dapk.st.core.ThemeStore
import app.dapk.st.core.isAtLeastS
import app.dapk.st.domain.eventlog.LoggingStore
import app.dapk.st.push.PushTokenRegistrars import app.dapk.st.push.PushTokenRegistrars
internal class SettingsItemFactory( internal class SettingsItemFactory(
@ -8,14 +12,14 @@ internal class SettingsItemFactory(
private val deviceMeta: DeviceMeta, private val deviceMeta: DeviceMeta,
private val pushTokenRegistrars: PushTokenRegistrars, private val pushTokenRegistrars: PushTokenRegistrars,
private val themeStore: ThemeStore, private val themeStore: ThemeStore,
private val loggingStore: LoggingStore,
) { ) {
suspend fun root() = general() + theme() + data() + account() + about() suspend fun root() = general() + theme() + data() + account() + advanced() + about()
private suspend fun general() = listOf( private suspend fun general() = listOf(
SettingItem.Header("General"), SettingItem.Header("General"),
SettingItem.Text(SettingItem.Id.Encryption, "Encryption"), SettingItem.Text(SettingItem.Id.Encryption, "Encryption"),
SettingItem.Text(SettingItem.Id.EventLog, "Event log"),
SettingItem.Text(SettingItem.Id.PushProvider, "Push provider", pushTokenRegistrars.currentSelection().id) SettingItem.Text(SettingItem.Id.PushProvider, "Push provider", pushTokenRegistrars.currentSelection().id)
) )
@ -36,6 +40,15 @@ internal class SettingsItemFactory(
SettingItem.Text(SettingItem.Id.SignOut, "Sign out"), SettingItem.Text(SettingItem.Id.SignOut, "Sign out"),
) )
private suspend fun advanced(): List<SettingItem> {
val loggingIsEnabled = loggingStore.isEnabled()
return listOf(
SettingItem.Header("Advanced"),
SettingItem.Toggle(SettingItem.Id.ToggleEnableLogs, "Enable local logging", state = loggingIsEnabled),
SettingItem.Text(SettingItem.Id.EventLog, "Event log", enabled = loggingIsEnabled),
)
}
private fun about() = listOf( private fun about() = listOf(
SettingItem.Header("About"), SettingItem.Header("About"),
SettingItem.Text(SettingItem.Id.PrivacyPolicy, "Privacy policy"), SettingItem.Text(SettingItem.Id.PrivacyPolicy, "Privacy policy"),

View File

@ -3,6 +3,7 @@ package app.dapk.st.settings
import android.content.ContentResolver import android.content.ContentResolver
import app.dapk.st.core.* import app.dapk.st.core.*
import app.dapk.st.domain.StoreModule import app.dapk.st.domain.StoreModule
import app.dapk.st.domain.eventlog.LoggingStore
import app.dapk.st.matrix.crypto.CryptoService import app.dapk.st.matrix.crypto.CryptoService
import app.dapk.st.matrix.sync.SyncService import app.dapk.st.matrix.sync.SyncService
import app.dapk.st.push.PushModule import app.dapk.st.push.PushModule
@ -18,6 +19,7 @@ class SettingsModule(
private val deviceMeta: DeviceMeta, private val deviceMeta: DeviceMeta,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
private val themeStore: ThemeStore, private val themeStore: ThemeStore,
private val loggingStore: LoggingStore,
) : ProvidableModule { ) : ProvidableModule {
internal fun settingsViewModel(): SettingsViewModel { internal fun settingsViewModel(): SettingsViewModel {
@ -27,9 +29,10 @@ class SettingsModule(
cryptoService, cryptoService,
syncService, syncService,
UriFilenameResolver(contentResolver, coroutineDispatchers), UriFilenameResolver(contentResolver, coroutineDispatchers),
SettingsItemFactory(buildMeta, deviceMeta, pushModule.pushTokenRegistrars(), themeStore), SettingsItemFactory(buildMeta, deviceMeta, pushModule.pushTokenRegistrars(), themeStore, loggingStore),
pushModule.pushTokenRegistrars(), pushModule.pushTokenRegistrars(),
themeStore, themeStore,
loggingStore,
) )
} }

View File

@ -5,7 +5,6 @@ import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.LocalActivityResultRegistryOwner
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -197,11 +196,11 @@ private fun RootSettings(page: Page.Root, onClick: (SettingItem) -> Unit) {
items(content.value) { item -> items(content.value) { item ->
when (item) { when (item) {
is SettingItem.Text -> { is SettingItem.Text -> {
val itemOnClick = onClick.takeIf { item.id != SettingItem.Id.Ignored }?.let { val itemOnClick = onClick.takeIf {
{ it.invoke(item) } item.id != SettingItem.Id.Ignored && item.enabled
} }?.let { { it.invoke(item) } }
SettingsTextRow(item.content, item.subtitle, itemOnClick) SettingsTextRow(item.content, item.subtitle, itemOnClick, enabled = item.enabled)
} }
is SettingItem.AccessToken -> { is SettingItem.AccessToken -> {
@ -313,6 +312,7 @@ private fun SettingsViewModel.ObserveEvents(onSignOut: () -> Unit) {
is OpenUrl -> { is OpenUrl -> {
context.startActivity(Intent(Intent.ACTION_VIEW).apply { data = it.url.toUri() }) context.startActivity(Intent(Intent.ACTION_VIEW).apply { data = it.url.toUri() })
} }
RecreateActivity -> { RecreateActivity -> {
context.getActivity()?.recreate() context.getActivity()?.recreate()
} }

View File

@ -43,7 +43,7 @@ internal sealed interface SettingItem {
val id: Id val id: Id
data class Header(val label: String, override val id: Id = Id.Ignored) : SettingItem data class Header(val label: String, override val id: Id = Id.Ignored) : SettingItem
data class Text(override val id: Id, val content: String, val subtitle: String? = null) : SettingItem data class Text(override val id: Id, val content: String, val subtitle: String? = null, val enabled: Boolean = true) : SettingItem
data class Toggle(override val id: Id, val content: String, val state: Boolean) : SettingItem data class Toggle(override val id: Id, val content: String, val state: Boolean) : SettingItem
data class AccessToken(override val id: Id, val content: String, val accessToken: String) : SettingItem data class AccessToken(override val id: Id, val content: String, val accessToken: String) : SettingItem
@ -57,6 +57,7 @@ internal sealed interface SettingItem {
PrivacyPolicy, PrivacyPolicy,
Ignored, Ignored,
ToggleDynamicTheme, ToggleDynamicTheme,
ToggleEnableLogs,
} }
} }

View File

@ -7,6 +7,7 @@ import app.dapk.st.core.Lce
import app.dapk.st.core.ThemeStore import app.dapk.st.core.ThemeStore
import app.dapk.st.design.components.SpiderPage import app.dapk.st.design.components.SpiderPage
import app.dapk.st.domain.StoreCleaner import app.dapk.st.domain.StoreCleaner
import app.dapk.st.domain.eventlog.LoggingStore
import app.dapk.st.matrix.crypto.CryptoService import app.dapk.st.matrix.crypto.CryptoService
import app.dapk.st.matrix.crypto.ImportResult import app.dapk.st.matrix.crypto.ImportResult
import app.dapk.st.matrix.sync.SyncService import app.dapk.st.matrix.sync.SyncService
@ -32,6 +33,7 @@ internal class SettingsViewModel(
private val settingsItemFactory: SettingsItemFactory, private val settingsItemFactory: SettingsItemFactory,
private val pushTokenRegistrars: PushTokenRegistrars, private val pushTokenRegistrars: PushTokenRegistrars,
private val themeStore: ThemeStore, private val themeStore: ThemeStore,
private val loggingStore: LoggingStore,
factory: MutableStateFactory<SettingsScreenState> = defaultStateFactory(), factory: MutableStateFactory<SettingsScreenState> = defaultStateFactory(),
) : DapkViewModel<SettingsScreenState, SettingsEvent>( ) : DapkViewModel<SettingsScreenState, SettingsEvent>(
initialState = SettingsScreenState(SpiderPage(Page.Routes.root, "Settings", null, Page.Root(Lce.Loading()))), initialState = SettingsScreenState(SpiderPage(Page.Routes.root, "Settings", null, Page.Root(Lce.Loading()))),
@ -52,31 +54,23 @@ internal class SettingsViewModel(
fun onClick(item: SettingItem) { fun onClick(item: SettingItem) {
when (item.id) { when (item.id) {
SignOut -> { SignOut -> viewModelScope.launch {
viewModelScope.launch { cacheCleaner.cleanCache(removeCredentials = true)
cacheCleaner.cleanCache(removeCredentials = true) _events.emit(SignedOut)
_events.emit(SignedOut)
}
} }
AccessToken -> { AccessToken -> viewModelScope.launch {
viewModelScope.launch { require(item is SettingItem.AccessToken)
require(item is SettingItem.AccessToken) _events.emit(CopyToClipboard("Token copied", item.accessToken))
_events.emit(CopyToClipboard("Token copied", item.accessToken))
}
} }
ClearCache -> { ClearCache -> viewModelScope.launch {
viewModelScope.launch { cacheCleaner.cleanCache(removeCredentials = false)
cacheCleaner.cleanCache(removeCredentials = false) _events.emit(Toast(message = "Cache deleted"))
_events.emit(Toast(message = "Cache deleted"))
}
} }
EventLog -> { EventLog -> viewModelScope.launch {
viewModelScope.launch { _events.emit(OpenEventLog)
_events.emit(OpenEventLog)
}
} }
Encryption -> { Encryption -> {
@ -85,10 +79,8 @@ internal class SettingsViewModel(
} }
} }
PrivacyPolicy -> { PrivacyPolicy -> viewModelScope.launch {
viewModelScope.launch { _events.emit(OpenUrl(PRIVACY_POLICY_URL))
_events.emit(OpenUrl(PRIVACY_POLICY_URL))
}
} }
PushProvider -> { PushProvider -> {
@ -100,16 +92,24 @@ internal class SettingsViewModel(
Ignored -> { Ignored -> {
// do nothing // do nothing
} }
ToggleDynamicTheme -> {
viewModelScope.launch { ToggleDynamicTheme -> viewModelScope.launch {
themeStore.storeMaterialYouEnabled(!themeStore.isMaterialYouEnabled()) themeStore.storeMaterialYouEnabled(!themeStore.isMaterialYouEnabled())
start() refreshRoot()
_events.emit(RecreateActivity) _events.emit(RecreateActivity)
}
}
ToggleEnableLogs -> viewModelScope.launch {
loggingStore.setEnabled(!loggingStore.isEnabled())
refreshRoot()
} }
} }
} }
private fun refreshRoot() {
start()
}
fun fetchPushProviders() { fun fetchPushProviders() {
updatePageState<Page.PushProviders> { copy(options = Lce.Loading()) } updatePageState<Page.PushProviders> { copy(options = Lce.Loading()) }
@ -146,9 +146,11 @@ internal class SettingsViewModel(
is ImportResult.Error -> { is ImportResult.Error -> {
// do nothing // do nothing
} }
is ImportResult.Update -> { is ImportResult.Update -> {
// do nothing // do nothing
} }
is ImportResult.Success -> { is ImportResult.Success -> {
syncService.forceManualRefresh(it.roomIds.toList()) syncService.forceManualRefresh(it.roomIds.toList())
} }

View File

@ -0,0 +1,12 @@
package app.dapk.st.settings
import app.dapk.st.domain.eventlog.LoggingStore
import io.mockk.coEvery
import io.mockk.mockk
import test.delegateReturn
class FakeLoggingStore {
val instance = mockk<LoggingStore>()
fun givenLoggingIsEnabled() = coEvery { instance.isEnabled() }.delegateReturn()
}

View File

@ -1,6 +1,7 @@
package app.dapk.st.settings package app.dapk.st.settings
import app.dapk.st.core.ThemeStore import app.dapk.st.core.ThemeStore
import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import test.delegateReturn import test.delegateReturn
@ -8,5 +9,5 @@ import test.delegateReturn
class FakeThemeStore { class FakeThemeStore {
val instance = mockk<ThemeStore>() val instance = mockk<ThemeStore>()
fun givenMaterialYouIsEnabled() = every { instance.isMaterialYouEnabled() }.delegateReturn() fun givenMaterialYouIsEnabled() = coEvery { instance.isMaterialYouEnabled() }.delegateReturn()
} }

View File

@ -15,6 +15,7 @@ import test.delegateReturn
private val A_SELECTION = Registrar("A_SELECTION") private val A_SELECTION = Registrar("A_SELECTION")
private const val ENABLED_MATERIAL_YOU = true private const val ENABLED_MATERIAL_YOU = true
private const val DISABLED_LOGGING = false
class SettingsItemFactoryTest { class SettingsItemFactoryTest {
@ -22,20 +23,27 @@ class SettingsItemFactoryTest {
private val deviceMeta = DeviceMeta(apiVersion = 31) private val deviceMeta = DeviceMeta(apiVersion = 31)
private val fakePushTokenRegistrars = FakePushRegistrars() private val fakePushTokenRegistrars = FakePushRegistrars()
private val fakeThemeStore = FakeThemeStore() private val fakeThemeStore = FakeThemeStore()
private val fakeLoggingStore = FakeLoggingStore()
private val settingsItemFactory = SettingsItemFactory(buildMeta, deviceMeta, fakePushTokenRegistrars.instance, fakeThemeStore.instance) private val settingsItemFactory = SettingsItemFactory(
buildMeta,
deviceMeta,
fakePushTokenRegistrars.instance,
fakeThemeStore.instance,
fakeLoggingStore.instance
)
@Test @Test
fun `when creating root items, then is expected`() = runTest { fun `when creating root items, then is expected`() = runTest {
fakePushTokenRegistrars.givenCurrentSelection().returns(A_SELECTION) fakePushTokenRegistrars.givenCurrentSelection().returns(A_SELECTION)
fakeThemeStore.givenMaterialYouIsEnabled().returns(ENABLED_MATERIAL_YOU) fakeThemeStore.givenMaterialYouIsEnabled().returns(ENABLED_MATERIAL_YOU)
fakeLoggingStore.givenLoggingIsEnabled().returns(DISABLED_LOGGING)
val result = settingsItemFactory.root() val result = settingsItemFactory.root()
result shouldBeEqualTo listOf( result shouldBeEqualTo listOf(
aSettingHeaderItem("General"), aSettingHeaderItem("General"),
aSettingTextItem(SettingItem.Id.Encryption, "Encryption"), aSettingTextItem(SettingItem.Id.Encryption, "Encryption"),
aSettingTextItem(SettingItem.Id.EventLog, "Event log"),
aSettingTextItem(SettingItem.Id.PushProvider, "Push provider", A_SELECTION.id), aSettingTextItem(SettingItem.Id.PushProvider, "Push provider", A_SELECTION.id),
SettingItem.Header("Theme"), SettingItem.Header("Theme"),
SettingItem.Toggle(SettingItem.Id.ToggleDynamicTheme, "Enable Material You", state = ENABLED_MATERIAL_YOU), SettingItem.Toggle(SettingItem.Id.ToggleDynamicTheme, "Enable Material You", state = ENABLED_MATERIAL_YOU),
@ -43,6 +51,9 @@ class SettingsItemFactoryTest {
aSettingTextItem(SettingItem.Id.ClearCache, "Clear cache"), aSettingTextItem(SettingItem.Id.ClearCache, "Clear cache"),
aSettingHeaderItem("Account"), aSettingHeaderItem("Account"),
aSettingTextItem(SettingItem.Id.SignOut, "Sign out"), aSettingTextItem(SettingItem.Id.SignOut, "Sign out"),
aSettingHeaderItem("Advanced"),
SettingItem.Toggle(SettingItem.Id.ToggleEnableLogs, "Enable local logging", state = DISABLED_LOGGING),
aSettingTextItem(SettingItem.Id.EventLog, "Event log", enabled = DISABLED_LOGGING),
aSettingHeaderItem("About"), aSettingHeaderItem("About"),
aSettingTextItem(SettingItem.Id.PrivacyPolicy, "Privacy policy"), aSettingTextItem(SettingItem.Id.PrivacyPolicy, "Privacy policy"),
aSettingTextItem(SettingItem.Id.Ignored, "Version", buildMeta.versionName), aSettingTextItem(SettingItem.Id.Ignored, "Version", buildMeta.versionName),

View File

@ -41,6 +41,7 @@ internal class SettingsViewModelTest {
private val fakePushTokenRegistrars = FakePushRegistrars() private val fakePushTokenRegistrars = FakePushRegistrars()
private val fakeSettingsItemFactory = FakeSettingsItemFactory() private val fakeSettingsItemFactory = FakeSettingsItemFactory()
private val fakeThemeStore = FakeThemeStore() private val fakeThemeStore = FakeThemeStore()
private val fakeLoggingStore = FakeLoggingStore()
private val viewModel = SettingsViewModel( private val viewModel = SettingsViewModel(
fakeStoreCleaner, fakeStoreCleaner,
@ -51,6 +52,7 @@ internal class SettingsViewModelTest {
fakeSettingsItemFactory.instance, fakeSettingsItemFactory.instance,
fakePushTokenRegistrars.instance, fakePushTokenRegistrars.instance,
fakeThemeStore.instance, fakeThemeStore.instance,
fakeLoggingStore.instance,
runViewModelTest.testMutableStateFactory(), runViewModelTest.testMutableStateFactory(),
) )

View File

@ -5,8 +5,9 @@ import app.dapk.st.settings.SettingItem
internal fun aSettingTextItem( internal fun aSettingTextItem(
id: SettingItem.Id = SettingItem.Id.Ignored, id: SettingItem.Id = SettingItem.Id.Ignored,
content: String = "text-content", content: String = "text-content",
subtitle: String? = null subtitle: String? = null,
) = SettingItem.Text(id, content, subtitle) enabled: Boolean = true,
) = SettingItem.Text(id, content, subtitle, enabled)
internal fun aSettingHeaderItem( internal fun aSettingHeaderItem(
label: String = "header-label", label: String = "header-label",