From 17f3ed7aa7281d4e6a3dbb9d44d7c3fc81d6ce0d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 1 Sep 2023 16:26:39 +0200 Subject: [PATCH 1/3] Developer setting: add entry point to configure the Tracing. Developer setting: add screen to configure log level. Give the custom trace filter to the SDK. --- .../x/initializer/TracingInitializer.kt | 8 +- .../preferences/impl/PreferencesFlowNode.kt | 14 +- .../impl/developer/DeveloperSettingsNode.kt | 10 + .../impl/developer/DeveloperSettingsView.kt | 8 + .../tracing/ConfigureTracingEvents.kt | 25 ++ .../developer/tracing/ConfigureTracingNode.kt | 45 ++++ .../tracing/ConfigureTracingPresenter.kt | 53 ++++ .../tracing/ConfigureTracingState.kt | 25 ++ .../tracing/ConfigureTracingStateProvider.kt | 37 +++ .../developer/tracing/ConfigureTracingView.kt | 247 ++++++++++++++++++ .../tracing/TargetLogLevelMapBuilder.kt | 43 +++ .../tracing/TracingConfigurationStore.kt | 60 +++++ .../tracing/ConfigureTracingPresenterTest.kt | 104 ++++++++ .../InMemoryTracingConfigurationStore.kt | 44 ++++ .../api/tracing/TracingFilterConfiguration.kt | 45 ++-- 15 files changed, 746 insertions(+), 22 deletions(-) create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingEvents.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingNode.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TargetLogLevelMapBuilder.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TracingConfigurationStore.kt create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/InMemoryTracingConfigurationStore.kt diff --git a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt index 068d439994..7b93812d35 100644 --- a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt +++ b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt @@ -17,7 +17,10 @@ package io.element.android.x.initializer import android.content.Context +import androidx.preference.PreferenceManager import androidx.startup.Initializer +import io.element.android.features.preferences.impl.developer.tracing.SharedPrefTracingConfigurationStore +import io.element.android.features.preferences.impl.developer.tracing.TargetLogLevelMapBuilder import io.element.android.libraries.architecture.bindings import io.element.android.libraries.matrix.api.tracing.TracingConfiguration import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations @@ -34,8 +37,11 @@ class TracingInitializer : Initializer { val bugReporter = appBindings.bugReporter() Timber.plant(tracingService.createTimberTree()) val tracingConfiguration = if (BuildConfig.DEBUG) { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val store = SharedPrefTracingConfigurationStore(prefs) + val builder = TargetLogLevelMapBuilder(store) TracingConfiguration( - filterConfiguration = TracingFilterConfigurations.debug, + filterConfiguration = TracingFilterConfigurations.custom(builder.getCurrentMap()), writesToLogcat = true, writesToFilesConfiguration = WriteToFilesConfiguration.Disabled ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index 7e95e0035c..872520105f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -33,6 +33,7 @@ import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.preferences.impl.about.AboutNode import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode import io.element.android.features.preferences.impl.developer.DeveloperSettingsNode +import io.element.android.features.preferences.impl.developer.tracing.ConfigureTracingNode import io.element.android.features.preferences.impl.root.PreferencesRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler @@ -60,6 +61,9 @@ class PreferencesFlowNode @AssistedInject constructor( @Parcelize data object DeveloperSettings : NavTarget + @Parcelize + data object ConfigureTracing : NavTarget + @Parcelize data object AnalyticsSettings : NavTarget @@ -94,7 +98,15 @@ class PreferencesFlowNode @AssistedInject constructor( createNode(buildContext, plugins = listOf(callback)) } NavTarget.DeveloperSettings -> { - createNode(buildContext) + val callback = object : DeveloperSettingsNode.Callback { + override fun openConfigureTracing() { + backstack.push(NavTarget.ConfigureTracing) + } + } + createNode(buildContext, listOf(callback)) + } + NavTarget.ConfigureTracing -> { + createNode(buildContext) } NavTarget.About -> { createNode(buildContext) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt index d5af758dec..96339e7bb4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt @@ -24,6 +24,7 @@ import com.airbnb.android.showkase.models.Showkase import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode @@ -37,6 +38,14 @@ class DeveloperSettingsNode @AssistedInject constructor( private val presenter: DeveloperSettingsPresenter, ) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun openConfigureTracing() + } + + private fun onOpenConfigureTracing() { + plugins().forEach { it.openConfigureTracing() } + } + @Composable override fun View(modifier: Modifier) { val activity = LocalContext.current as Activity @@ -50,6 +59,7 @@ class DeveloperSettingsNode @AssistedInject constructor( state = state, modifier = modifier, onOpenShowkase = ::openShowkase, + onOpenConfigureTracing = ::onOpenConfigureTracing, onBackPressed = ::navigateUp ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 23ea4faf86..684587220b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -35,6 +35,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun DeveloperSettingsView( state: DeveloperSettingsState, onOpenShowkase: () -> Unit, + onOpenConfigureTracing: () -> Unit, onBackPressed: () -> Unit, modifier: Modifier = Modifier, ) { @@ -47,6 +48,12 @@ fun DeveloperSettingsView( PreferenceCategory(title = "Feature flags") { FeatureListContent(state) } + PreferenceCategory(title = "Rust SDK") { + PreferenceText( + title = "Configure tracing", + onClick = onOpenConfigureTracing, + ) + } PreferenceCategory(title = "Showkase") { PreferenceText( title = "Open Showkase browser", @@ -109,6 +116,7 @@ private fun ContentToPreview(state: DeveloperSettingsState) { DeveloperSettingsView( state = state, onOpenShowkase = {}, + onOpenConfigureTracing = {}, onBackPressed = {} ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingEvents.kt new file mode 100644 index 0000000000..2d73ea0726 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingEvents.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target + +sealed interface ConfigureTracingEvents { + data class UpdateFilter(val target: Target, val logLevel: LogLevel) : ConfigureTracingEvents + data object ResetFilters : ConfigureTracingEvents +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingNode.kt new file mode 100644 index 0000000000..7c58058798 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingNode.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.di.AppScope + +@ContributesNode(AppScope::class) +class ConfigureTracingNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: ConfigureTracingPresenter, +) : Node(buildContext, plugins = plugins) { + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + ConfigureTracingView( + state = state, + onBackPressed = ::navigateUp, + modifier = modifier + ) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt new file mode 100644 index 0000000000..6ad0529597 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import io.element.android.libraries.architecture.Presenter +import javax.inject.Inject + +class ConfigureTracingPresenter @Inject constructor( + private val tracingConfigurationStore: TracingConfigurationStore, + private val targetLogLevelMapBuilder: TargetLogLevelMapBuilder, +) : Presenter { + + @Composable + override fun present(): ConfigureTracingState { + val modifiedMap = remember { mutableStateOf(targetLogLevelMapBuilder.getCurrentMap()) } + + fun handleEvents(event: ConfigureTracingEvents) { + when (event) { + is ConfigureTracingEvents.UpdateFilter -> { + modifiedMap.value = modifiedMap.value.toMutableMap() + .apply { this[event.target] = event.logLevel } + tracingConfigurationStore.storeLogLevel(event.target, event.logLevel) + } + ConfigureTracingEvents.ResetFilters -> { + modifiedMap.value = targetLogLevelMapBuilder.getDefaultMap() + tracingConfigurationStore.reset() + } + } + } + + return ConfigureTracingState( + targetsToLogLevel = modifiedMap.value, + eventSink = ::handleEvents + ) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt new file mode 100644 index 0000000000..f8433a68fb --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target + +data class ConfigureTracingState( + val targetsToLogLevel: Map, + val eventSink: (ConfigureTracingEvents) -> Unit +) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt new file mode 100644 index 0000000000..06bacb8e74 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target + +open class ConfigureTracingStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aConfigureTracingState(), + ) +} + +fun aConfigureTracingState() = ConfigureTracingState( + targetsToLogLevel = mapOf( + Target.COMMON to LogLevel.INFO, + Target.MATRIX_SDK_FFI to LogLevel.WARN, + Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.ERROR, + ), + eventSink = {} +) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt new file mode 100644 index 0000000000..bf311e7e22 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingView.kt @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.ArrowDropUp +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.list.ListItemContent +import io.element.android.libraries.designsystem.preview.DayNightPreviews +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.DropdownMenu +import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target +import io.element.android.libraries.theme.ElementTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ConfigureTracingView( + state: ConfigureTracingState, + onBackPressed: () -> Unit, + modifier: Modifier = Modifier, +) { + var showMenu by remember { mutableStateOf(false) } + Scaffold( + modifier = modifier + .fillMaxSize() + .systemBarsPadding() + .imePadding(), + contentWindowInsets = WindowInsets.statusBars, + topBar = { + TopAppBar( + modifier = modifier, + navigationIcon = { + BackButton(onClick = onBackPressed) + }, + title = { + Text( + text = "Configure tracing", + style = ElementTheme.typography.aliasScreenTitle, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + actions = { + IconButton( + onClick = { showMenu = !showMenu } + ) { + Icon( + imageVector = Icons.Default.MoreVert, + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem( + onClick = { + showMenu = false + state.eventSink.invoke(ConfigureTracingEvents.ResetFilters) + }, + text = { Text("Reset to default") }, + leadingIcon = { + Icon( + Icons.Outlined.Delete, + tint = ElementTheme.materialColors.secondary, + contentDescription = null, + ) + } + ) + } + } + ) + }, + content = { + Column( + modifier = Modifier + .padding(it) + .consumeWindowInsets(it) + .verticalScroll(state = rememberScrollState()) + ) { + CrateListContent(state) + ListItem( + headlineContent = { + Text( + text = "Kill and restart the app for the change to take effect.", + style = ElementTheme.typography.fontHeadingSmMedium, + ) + }, + ) + } + } + ) +} + +@Composable +fun CrateListContent( + state: ConfigureTracingState, + modifier: Modifier = Modifier +) { + fun onLogLevelChange(target: Target, logLevel: LogLevel) { + state.eventSink(ConfigureTracingEvents.UpdateFilter(target, logLevel)) + } + + TargetAndLogLevelListView( + modifier = modifier, + data = state.targetsToLogLevel, + onLogLevelChange = ::onLogLevelChange, + ) +} + +@Composable +private fun TargetAndLogLevelListView( + data: Map, + onLogLevelChange: (Target, LogLevel) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + ) { + data.forEach { item -> + fun onLogLevelChange(logLevel: LogLevel) { + onLogLevelChange(item.key, logLevel) + } + + TargetAndLogLevelView( + target = item.key, + logLevel = item.value, + onLogLevelChange = ::onLogLevelChange + ) + } + } +} + +@Composable +fun TargetAndLogLevelView( + target: Target, + logLevel: LogLevel, + onLogLevelChange: (LogLevel) -> Unit, + modifier: Modifier = Modifier +) { + ListItem( + modifier = modifier, + headlineContent = { Text(text = target.filter.takeIf { it.isNotEmpty() } ?: "(common)") }, + trailingContent = ListItemContent.Custom { + LogLevelDropdownMenu( + logLevel = logLevel, + onLogLevelChange = onLogLevelChange, + ) + }, + ) +} + +@Composable +fun LogLevelDropdownMenu( + logLevel: LogLevel, + onLogLevelChange: (LogLevel) -> Unit, + modifier: Modifier = Modifier, +) { + var expanded by remember { mutableStateOf(false) } + DropdownMenuItem( + modifier = modifier.widthIn(max = 120.dp), + text = { Text(text = logLevel.filter) }, + onClick = { expanded = !expanded }, + trailingIcon = { + if (expanded) { + Icon(Icons.Default.ArrowDropUp, contentDescription = null) + } else { + Icon(Icons.Default.ArrowDropDown, contentDescription = null) + } + }, + ) + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + LogLevel.values().forEach { logLevel -> + DropdownMenuItem( + text = { + Text(text = logLevel.filter) + }, + onClick = { + expanded = false + onLogLevelChange(logLevel) + } + ) + } + } +} + +@DayNightPreviews +@Composable +internal fun ConfigureTracingViewPreview( + @PreviewParameter(ConfigureTracingStateProvider::class) state: ConfigureTracingState +) = ElementPreview { + ConfigureTracingView( + state = state, + onBackPressed = {}, + ) +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TargetLogLevelMapBuilder.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TargetLogLevelMapBuilder.kt new file mode 100644 index 0000000000..c70d573430 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TargetLogLevelMapBuilder.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target +import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations +import javax.inject.Inject + +class TargetLogLevelMapBuilder @Inject constructor( + private val tracingConfigurationStore: TracingConfigurationStore, +) { + private val defaultConfig = TracingFilterConfigurations.debug + + fun getDefaultMap(): Map { + return Target.entries.associateWith { target -> + defaultConfig.getLogLevel(target) + ?: LogLevel.INFO + } + } + + fun getCurrentMap(): Map { + return Target.entries.associateWith { target -> + tracingConfigurationStore.getLogLevel(target) + ?: defaultConfig.getLogLevel(target) + ?: LogLevel.INFO + } + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TracingConfigurationStore.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TracingConfigurationStore.kt new file mode 100644 index 0000000000..582231eb50 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/TracingConfigurationStore.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import android.content.SharedPreferences +import androidx.core.content.edit +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.DefaultPreferences +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target +import javax.inject.Inject + +interface TracingConfigurationStore { + fun getLogLevel(target: Target): LogLevel? + fun storeLogLevel(target: Target, logLevel: LogLevel) + fun reset() +} + +@ContributesBinding(AppScope::class) +class SharedPrefTracingConfigurationStore @Inject constructor( + @DefaultPreferences private val sharedPreferences: SharedPreferences +) : TracingConfigurationStore { + override fun getLogLevel(target: Target): LogLevel? { + return sharedPreferences.getString("$KEY_PREFIX${target.name}", null) + ?.let { LogLevel.valueOf(it) } + } + + override fun storeLogLevel(target: Target, logLevel: LogLevel) { + sharedPreferences.edit { + putString("$KEY_PREFIX${target.name}", logLevel.name) + } + } + + override fun reset() { + sharedPreferences.edit { + sharedPreferences.all.keys.filter { it.startsWith(KEY_PREFIX) }.forEach { + remove(it) + } + } + } + + companion object { + private const val KEY_PREFIX = "tracing_log_level_" + } +} diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt new file mode 100644 index 0000000000..979713427e --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenterTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target +import io.element.android.tests.testutils.waitForPredicate +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class ConfigureTracingPresenterTest { + @Test + fun `present - initial state`() = runTest { + val store = InMemoryTracingConfigurationStore() + val presenter = ConfigureTracingPresenter( + store, + TargetLogLevelMapBuilder(store), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.targetsToLogLevel).isNotEmpty() + assertThat(initialState.targetsToLogLevel[Target.MATRIX_SDK_CRYPTO]).isEqualTo(LogLevel.DEBUG) + } + } + + @Test + fun `present - store is taken into account`() = runTest { + val store = InMemoryTracingConfigurationStore() + store.givenLogLevel(LogLevel.ERROR) + val presenter = ConfigureTracingPresenter( + store, + TargetLogLevelMapBuilder(store), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.targetsToLogLevel).isNotEmpty() + assertThat(initialState.targetsToLogLevel[Target.MATRIX_SDK_CRYPTO]).isEqualTo(LogLevel.ERROR) + } + } + + @Test + fun `present - change a value`() = runTest { + val store = InMemoryTracingConfigurationStore() + val presenter = ConfigureTracingPresenter( + store, + TargetLogLevelMapBuilder(store), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.targetsToLogLevel[Target.MATRIX_SDK_CRYPTO]).isEqualTo(LogLevel.DEBUG) + initialState.eventSink.invoke(ConfigureTracingEvents.UpdateFilter(Target.MATRIX_SDK_CRYPTO, LogLevel.WARN)) + val finalState = awaitItem() + assertThat(finalState.targetsToLogLevel[Target.MATRIX_SDK_CRYPTO]).isEqualTo(LogLevel.WARN) + waitForPredicate { store.hasStoreLogLevelBeenCalled } + } + } + + @Test + fun `present - reset`() = runTest { + val store = InMemoryTracingConfigurationStore() + val presenter = ConfigureTracingPresenter( + store, + TargetLogLevelMapBuilder(store), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.targetsToLogLevel[Target.MATRIX_SDK_CRYPTO]).isEqualTo(LogLevel.DEBUG) + initialState.eventSink.invoke(ConfigureTracingEvents.UpdateFilter(Target.MATRIX_SDK_CRYPTO, LogLevel.WARN)) + val finalState = awaitItem() + assertThat(finalState.targetsToLogLevel[Target.MATRIX_SDK_CRYPTO]).isEqualTo(LogLevel.WARN) + waitForPredicate { store.hasStoreLogLevelBeenCalled } + finalState.eventSink.invoke(ConfigureTracingEvents.ResetFilters) + val resetState = awaitItem() + assertThat(resetState.targetsToLogLevel[Target.MATRIX_SDK_CRYPTO]).isEqualTo(LogLevel.DEBUG) + waitForPredicate { store.hasResetBeenCalled } + } + } +} diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/InMemoryTracingConfigurationStore.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/InMemoryTracingConfigurationStore.kt new file mode 100644 index 0000000000..1d17a1227b --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/tracing/InMemoryTracingConfigurationStore.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.developer.tracing + +import io.element.android.libraries.matrix.api.tracing.LogLevel +import io.element.android.libraries.matrix.api.tracing.Target + +class InMemoryTracingConfigurationStore : TracingConfigurationStore { + var hasResetBeenCalled = false + private set + var hasStoreLogLevelBeenCalled = false + private set + private var logLevel: LogLevel? = null + + fun givenLogLevel(logLevel: LogLevel?) { + this.logLevel = logLevel + } + + override fun getLogLevel(target: Target): LogLevel? { + return logLevel + } + + override fun storeLogLevel(target: Target, logLevel: LogLevel) { + hasStoreLogLevelBeenCalled = true + } + + override fun reset() { + hasResetBeenCalled = true + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt index 596b611296..d34911644e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt @@ -21,22 +21,27 @@ data class TracingFilterConfiguration( ) { // Order should matters - private val targetsToLogLevel: MutableMap = mutableMapOf( - Target.COMMON to LogLevel.Info, - Target.HYPER to LogLevel.Warn, - Target.MATRIX_SDK_CRYPTO to LogLevel.Debug, - Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.Debug, - Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.Trace, - Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.Trace, - Target.MATRIX_SDK_UI_TIMELINE to LogLevel.Info, + private val targetsToLogLevel: Map = mapOf( + Target.COMMON to LogLevel.INFO, + Target.HYPER to LogLevel.WARN, + Target.MATRIX_SDK_CRYPTO to LogLevel.DEBUG, + Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.DEBUG, + Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.TRACE, + Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.TRACE, + Target.MATRIX_SDK_UI_TIMELINE to LogLevel.INFO, ) + fun getLogLevel(target: Target): LogLevel? { + return overrides[target] ?: targetsToLogLevel[target] + } + val filter: String get() { + val fullMap = targetsToLogLevel.toMutableMap() overrides.forEach { (target, logLevel) -> - targetsToLogLevel[target] = logLevel + fullMap[target] = logLevel } - return targetsToLogLevel.map { + return fullMap.map { if (it.key.filter.isEmpty()) { it.value.filter } else { @@ -59,25 +64,25 @@ enum class Target(open val filter: String) { MATRIX_SDK_UI_TIMELINE("matrix_sdk_ui::timeline"), } -sealed class LogLevel(val filter: String) { - data object Warn : LogLevel("warn") - data object Trace : LogLevel("trace") - data object Info : LogLevel("info") - data object Debug : LogLevel("debug") - data object Error : LogLevel("error") +enum class LogLevel(open val filter: String) { + ERROR("error"), + WARN("warn"), + INFO("info"), + DEBUG("debug"), + TRACE("trace"), } object TracingFilterConfigurations { val release = TracingFilterConfiguration( overrides = mapOf( - Target.COMMON to LogLevel.Info, - Target.ELEMENT to LogLevel.Debug + Target.COMMON to LogLevel.INFO, + Target.ELEMENT to LogLevel.DEBUG ), ) val debug = TracingFilterConfiguration( overrides = mapOf( - Target.COMMON to LogLevel.Info, - Target.ELEMENT to LogLevel.Trace + Target.COMMON to LogLevel.INFO, + Target.ELEMENT to LogLevel.TRACE ) ) From 2128bfe8535b58dd93960402a0bd076143b891a9 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 1 Sep 2023 14:39:03 +0000 Subject: [PATCH 2/3] Update screenshots --- ...null_ConfigureTracingView-D-0_1_null_0,NEXUS_5,1.0,en].png | 3 +++ ...null_ConfigureTracingView-N-0_2_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ull_DeveloperSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ull_DeveloperSettingsViewDark_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ll_DeveloperSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ll_DeveloperSettingsViewLight_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- 6 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-0_1_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-0_2_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-0_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..acf8d934bb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-D-0_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e55b03652dc0149cdc2623fa81b678a68a8080f57043c1f71cc99565e44c98e0 +size 35025 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-0_2_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2a8c0680e3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_null_ConfigureTracingView-N-0_2_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e38e363ecdec4e70699204a83a01ac3e4a840f20f1ebc84bd014b49e0e52c77b +size 31291 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png index 29125d81e3..9dbdf12053 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ecff1b486e8aca39a5b7c81139e1132829d9baded8888977eeaa88fd1a6e5f2 -size 49665 +oid sha256:e6b1bfbd1d8c29347433e0c8a1037ea219b0935f9a74196f4c96952d144417e9 +size 48845 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_1,NEXUS_5,1.0,en].png index 29125d81e3..9dbdf12053 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewDark_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ecff1b486e8aca39a5b7c81139e1132829d9baded8888977eeaa88fd1a6e5f2 -size 49665 +oid sha256:e6b1bfbd1d8c29347433e0c8a1037ea219b0935f9a74196f4c96952d144417e9 +size 48845 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png index be5e358d49..c1ebd00c8e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a2123c483c9609b3341c762907e1eb5f50052faa3731454ff0953cdb862809c -size 54357 +oid sha256:6814348aebbbb561fe42ae5e7c5ae9bec77cc7838b41adbc5158954b338fb0d1 +size 53755 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_1,NEXUS_5,1.0,en].png index be5e358d49..c1ebd00c8e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_null_DeveloperSettingsViewLight_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a2123c483c9609b3341c762907e1eb5f50052faa3731454ff0953cdb862809c -size 54357 +oid sha256:6814348aebbbb561fe42ae5e7c5ae9bec77cc7838b41adbc5158954b338fb0d1 +size 53755 From 8a46faeaf86bd228d5ac149c7f98db121d732d0d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 1 Sep 2023 17:39:46 +0200 Subject: [PATCH 3/3] Fix detekted issues. --- .../tracing/ConfigureTracingPresenter.kt | 3 +- .../tracing/ConfigureTracingState.kt | 3 +- .../tracing/ConfigureTracingStateProvider.kt | 3 +- .../developer/tracing/ConfigureTracingView.kt | 57 ++++++++++--------- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt index 6ad0529597..b0d2243c70 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingPresenter.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import io.element.android.libraries.architecture.Presenter +import kotlinx.collections.immutable.toImmutableMap import javax.inject.Inject class ConfigureTracingPresenter @Inject constructor( @@ -46,7 +47,7 @@ class ConfigureTracingPresenter @Inject constructor( } return ConfigureTracingState( - targetsToLogLevel = modifiedMap.value, + targetsToLogLevel = modifiedMap.value.toImmutableMap(), eventSink = ::handleEvents ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt index f8433a68fb..bc36a6ede3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingState.kt @@ -18,8 +18,9 @@ package io.element.android.features.preferences.impl.developer.tracing import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.Target +import kotlinx.collections.immutable.ImmutableMap data class ConfigureTracingState( - val targetsToLogLevel: Map, + val targetsToLogLevel: ImmutableMap, val eventSink: (ConfigureTracingEvents) -> Unit ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt index 06bacb8e74..fe2cfa44a9 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/tracing/ConfigureTracingStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.preferences.impl.developer.tracing import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.Target +import kotlinx.collections.immutable.persistentMapOf open class ConfigureTracingStateProvider : PreviewParameterProvider { override val values: Sequence @@ -28,7 +29,7 @@ open class ConfigureTracingStateProvider : PreviewParameterProvider, + data: ImmutableMap, onLogLevelChange: (Target, LogLevel) -> Unit, modifier: Modifier = Modifier, ) { @@ -205,32 +206,34 @@ fun LogLevelDropdownMenu( modifier: Modifier = Modifier, ) { var expanded by remember { mutableStateOf(false) } - DropdownMenuItem( - modifier = modifier.widthIn(max = 120.dp), - text = { Text(text = logLevel.filter) }, - onClick = { expanded = !expanded }, - trailingIcon = { - if (expanded) { - Icon(Icons.Default.ArrowDropUp, contentDescription = null) - } else { - Icon(Icons.Default.ArrowDropDown, contentDescription = null) - } - }, - ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - LogLevel.values().forEach { logLevel -> - DropdownMenuItem( - text = { - Text(text = logLevel.filter) - }, - onClick = { - expanded = false - onLogLevelChange(logLevel) + Box(modifier = modifier) { + DropdownMenuItem( + modifier = Modifier.widthIn(max = 120.dp), + text = { Text(text = logLevel.filter) }, + onClick = { expanded = !expanded }, + trailingIcon = { + if (expanded) { + Icon(Icons.Default.ArrowDropUp, contentDescription = null) + } else { + Icon(Icons.Default.ArrowDropDown, contentDescription = null) } - ) + }, + ) + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + LogLevel.values().forEach { logLevel -> + DropdownMenuItem( + text = { + Text(text = logLevel.filter) + }, + onClick = { + expanded = false + onLogLevelChange(logLevel) + } + ) + } } } }