Merge pull request #4158 from element-hq/feature/fga/update_rust_sdk_0.2.75
update rust sdk 0.2.75
This commit is contained in:
commit
9165b18185
60 changed files with 479 additions and 1017 deletions
|
|
@ -9,50 +9,34 @@ package io.element.android.x.initializer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.startup.Initializer
|
import androidx.startup.Initializer
|
||||||
import io.element.android.features.preferences.impl.developer.tracing.SharedPreferencesTracingConfigurationStore
|
|
||||||
import io.element.android.features.preferences.impl.developer.tracing.TargetLogLevelMapBuilder
|
|
||||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||||
import io.element.android.libraries.architecture.bindings
|
import io.element.android.libraries.architecture.bindings
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations
|
|
||||||
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
||||||
import io.element.android.x.BuildConfig
|
import io.element.android.x.BuildConfig
|
||||||
import io.element.android.x.di.AppBindings
|
import io.element.android.x.di.AppBindings
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
private const val ELEMENT_X_TARGET = "elementx"
|
||||||
|
|
||||||
class TracingInitializer : Initializer<Unit> {
|
class TracingInitializer : Initializer<Unit> {
|
||||||
override fun create(context: Context) {
|
override fun create(context: Context) {
|
||||||
val appBindings = context.bindings<AppBindings>()
|
val appBindings = context.bindings<AppBindings>()
|
||||||
val tracingService = appBindings.tracingService()
|
val tracingService = appBindings.tracingService()
|
||||||
val bugReporter = appBindings.bugReporter()
|
val bugReporter = appBindings.bugReporter()
|
||||||
Timber.plant(tracingService.createTimberTree())
|
Timber.plant(tracingService.createTimberTree(ELEMENT_X_TARGET))
|
||||||
val tracingConfiguration = if (BuildConfig.BUILD_TYPE == BuildType.RELEASE.name) {
|
val preferencesStore = appBindings.preferencesStore()
|
||||||
TracingConfiguration(
|
val logLevel = runBlocking { preferencesStore.getTracingLogLevelFlow().first() }
|
||||||
filterConfiguration = TracingFilterConfigurations.release,
|
val tracingConfiguration = TracingConfiguration(
|
||||||
writesToLogcat = false,
|
writesToLogcat = BuildConfig.DEBUG,
|
||||||
writesToFilesConfiguration = defaultWriteToDiskConfiguration(bugReporter),
|
writesToFilesConfiguration = defaultWriteToDiskConfiguration(bugReporter),
|
||||||
)
|
logLevel = logLevel,
|
||||||
} else {
|
extraTargets = listOf(ELEMENT_X_TARGET),
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
)
|
||||||
val store = SharedPreferencesTracingConfigurationStore(prefs)
|
bugReporter.setCurrentTracingLogLevel(logLevel.name)
|
||||||
val builder = TargetLogLevelMapBuilder(
|
|
||||||
tracingConfigurationStore = store,
|
|
||||||
defaultConfig = if (BuildConfig.BUILD_TYPE == BuildType.NIGHTLY.name) {
|
|
||||||
TracingFilterConfigurations.nightly
|
|
||||||
} else {
|
|
||||||
TracingFilterConfigurations.debug
|
|
||||||
}
|
|
||||||
)
|
|
||||||
TracingConfiguration(
|
|
||||||
filterConfiguration = TracingFilterConfigurations.custom(builder.getCurrentMap()),
|
|
||||||
writesToLogcat = BuildConfig.DEBUG,
|
|
||||||
writesToFilesConfiguration = defaultWriteToDiskConfiguration(bugReporter),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
bugReporter.setCurrentTracingFilter(tracingConfiguration.filterConfiguration.filter)
|
|
||||||
tracingService.setupTracing(tracingConfiguration)
|
tracingService.setupTracing(tracingConfiguration)
|
||||||
// Also set env variable for rust back trace
|
// Also set env variable for rust back trace
|
||||||
Os.setenv("RUST_BACKTRACE", "1", true)
|
Os.setenv("RUST_BACKTRACE", "1", true)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import io.element.android.anvilannotations.ContributesNode
|
||||||
import io.element.android.features.login.api.LoginEntryPoint
|
import io.element.android.features.login.api.LoginEntryPoint
|
||||||
import io.element.android.features.login.api.LoginFlowType
|
import io.element.android.features.login.api.LoginFlowType
|
||||||
import io.element.android.features.onboarding.api.OnBoardingEntryPoint
|
import io.element.android.features.onboarding.api.OnBoardingEntryPoint
|
||||||
import io.element.android.features.preferences.api.ConfigureTracingEntryPoint
|
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackView
|
||||||
import io.element.android.libraries.architecture.BaseFlowNode
|
import io.element.android.libraries.architecture.BaseFlowNode
|
||||||
import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices
|
import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices
|
||||||
|
|
@ -38,7 +37,6 @@ class NotLoggedInFlowNode @AssistedInject constructor(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
private val onBoardingEntryPoint: OnBoardingEntryPoint,
|
private val onBoardingEntryPoint: OnBoardingEntryPoint,
|
||||||
private val configureTracingEntryPoint: ConfigureTracingEntryPoint,
|
|
||||||
private val loginEntryPoint: LoginEntryPoint,
|
private val loginEntryPoint: LoginEntryPoint,
|
||||||
private val notLoggedInImageLoaderFactory: NotLoggedInImageLoaderFactory,
|
private val notLoggedInImageLoaderFactory: NotLoggedInImageLoaderFactory,
|
||||||
) : BaseFlowNode<NotLoggedInFlowNode.NavTarget>(
|
) : BaseFlowNode<NotLoggedInFlowNode.NavTarget>(
|
||||||
|
|
@ -68,9 +66,6 @@ class NotLoggedInFlowNode @AssistedInject constructor(
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class LoginFlow(val type: LoginFlowType) : NavTarget
|
data class LoginFlow(val type: LoginFlowType) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data object ConfigureTracing : NavTarget
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
|
|
@ -89,10 +84,6 @@ class NotLoggedInFlowNode @AssistedInject constructor(
|
||||||
backstack.push(NavTarget.LoginFlow(type = LoginFlowType.SIGN_IN_QR_CODE))
|
backstack.push(NavTarget.LoginFlow(type = LoginFlowType.SIGN_IN_QR_CODE))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenDeveloperSettings() {
|
|
||||||
backstack.push(NavTarget.ConfigureTracing)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReportProblem() {
|
override fun onReportProblem() {
|
||||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
plugins<Callback>().forEach { it.onOpenBugReport() }
|
||||||
}
|
}
|
||||||
|
|
@ -107,9 +98,6 @@ class NotLoggedInFlowNode @AssistedInject constructor(
|
||||||
.params(LoginEntryPoint.Params(flowType = navTarget.type))
|
.params(LoginEntryPoint.Params(flowType = navTarget.type))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
NavTarget.ConfigureTracing -> {
|
|
||||||
configureTracingEntryPoint.createNode(this, buildContext)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ interface OnBoardingEntryPoint : FeatureEntryPoint {
|
||||||
fun onSignUp()
|
fun onSignUp()
|
||||||
fun onSignIn()
|
fun onSignIn()
|
||||||
fun onSignInWithQrCode()
|
fun onSignInWithQrCode()
|
||||||
fun onOpenDeveloperSettings()
|
|
||||||
fun onReportProblem()
|
fun onReportProblem()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,6 @@ class OnBoardingNode @AssistedInject constructor(
|
||||||
plugins<OnBoardingEntryPoint.Callback>().forEach { it.onSignInWithQrCode() }
|
plugins<OnBoardingEntryPoint.Callback>().forEach { it.onSignInWithQrCode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onOpenDeveloperSettings() {
|
|
||||||
plugins<OnBoardingEntryPoint.Callback>().forEach { it.onOpenDeveloperSettings() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onReportProblem() {
|
private fun onReportProblem() {
|
||||||
plugins<OnBoardingEntryPoint.Callback>().forEach { it.onReportProblem() }
|
plugins<OnBoardingEntryPoint.Callback>().forEach { it.onReportProblem() }
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +53,6 @@ class OnBoardingNode @AssistedInject constructor(
|
||||||
onSignIn = ::onSignIn,
|
onSignIn = ::onSignIn,
|
||||||
onCreateAccount = ::onSignUp,
|
onCreateAccount = ::onSignUp,
|
||||||
onSignInWithQrCode = ::onSignInWithQrCode,
|
onSignInWithQrCode = ::onSignInWithQrCode,
|
||||||
onOpenDeveloperSettings = ::onOpenDeveloperSettings,
|
|
||||||
onReportProblem = ::onReportProblem,
|
onReportProblem = ::onReportProblem,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import androidx.compose.runtime.produceState
|
||||||
import io.element.android.appconfig.OnBoardingConfig
|
import io.element.android.appconfig.OnBoardingConfig
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -31,8 +30,7 @@ class OnBoardingPresenter @Inject constructor(
|
||||||
val canLoginWithQrCode by produceState(initialValue = false) {
|
val canLoginWithQrCode by produceState(initialValue = false) {
|
||||||
value = featureFlagService.isFeatureEnabled(FeatureFlags.QrCodeLogin)
|
value = featureFlagService.isFeatureEnabled(FeatureFlags.QrCodeLogin)
|
||||||
}
|
}
|
||||||
return OnBoardingState(
|
return OnBoardingState(
|
||||||
isDebugBuild = buildMeta.buildType != BuildType.RELEASE,
|
|
||||||
productionApplicationName = buildMeta.productionApplicationName,
|
productionApplicationName = buildMeta.productionApplicationName,
|
||||||
canLoginWithQrCode = canLoginWithQrCode,
|
canLoginWithQrCode = canLoginWithQrCode,
|
||||||
canCreateAccount = OnBoardingConfig.CAN_CREATE_ACCOUNT,
|
canCreateAccount = OnBoardingConfig.CAN_CREATE_ACCOUNT,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
package io.element.android.features.onboarding.impl
|
package io.element.android.features.onboarding.impl
|
||||||
|
|
||||||
data class OnBoardingState(
|
data class OnBoardingState(
|
||||||
val isDebugBuild: Boolean,
|
|
||||||
val productionApplicationName: String,
|
val productionApplicationName: String,
|
||||||
val canLoginWithQrCode: Boolean,
|
val canLoginWithQrCode: Boolean,
|
||||||
val canCreateAccount: Boolean,
|
val canCreateAccount: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,14 @@ open class OnBoardingStateProvider : PreviewParameterProvider<OnBoardingState> {
|
||||||
anOnBoardingState(canLoginWithQrCode = true),
|
anOnBoardingState(canLoginWithQrCode = true),
|
||||||
anOnBoardingState(canCreateAccount = true),
|
anOnBoardingState(canCreateAccount = true),
|
||||||
anOnBoardingState(canLoginWithQrCode = true, canCreateAccount = true),
|
anOnBoardingState(canLoginWithQrCode = true, canCreateAccount = true),
|
||||||
anOnBoardingState(isDebugBuild = true),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun anOnBoardingState(
|
fun anOnBoardingState(
|
||||||
isDebugBuild: Boolean = false,
|
|
||||||
productionApplicationName: String = "Element",
|
productionApplicationName: String = "Element",
|
||||||
canLoginWithQrCode: Boolean = false,
|
canLoginWithQrCode: Boolean = false,
|
||||||
canCreateAccount: Boolean = false
|
canCreateAccount: Boolean = false
|
||||||
) = OnBoardingState(
|
) = OnBoardingState(
|
||||||
isDebugBuild = isDebugBuild,
|
|
||||||
productionApplicationName = productionApplicationName,
|
productionApplicationName = productionApplicationName,
|
||||||
canLoginWithQrCode = canLoginWithQrCode,
|
canLoginWithQrCode = canLoginWithQrCode,
|
||||||
canCreateAccount = canCreateAccount
|
canCreateAccount = canCreateAccount
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.QrCode
|
import androidx.compose.material.icons.filled.QrCode
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||||
import androidx.compose.ui.BiasAlignment
|
import androidx.compose.ui.BiasAlignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -28,7 +27,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom
|
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtom
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize
|
import io.element.android.libraries.designsystem.atomic.atoms.ElementLogoAtomSize
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule
|
||||||
|
|
@ -36,8 +34,6 @@ import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Button
|
import io.element.android.libraries.designsystem.theme.components.Button
|
||||||
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.IconSource
|
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||||
|
|
@ -56,17 +52,13 @@ fun OnBoardingView(
|
||||||
onSignInWithQrCode: () -> Unit,
|
onSignInWithQrCode: () -> Unit,
|
||||||
onSignIn: () -> Unit,
|
onSignIn: () -> Unit,
|
||||||
onCreateAccount: () -> Unit,
|
onCreateAccount: () -> Unit,
|
||||||
onOpenDeveloperSettings: () -> Unit,
|
|
||||||
onReportProblem: () -> Unit,
|
onReportProblem: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
OnBoardingPage(
|
OnBoardingPage(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
content = {
|
content = {
|
||||||
OnBoardingContent(
|
OnBoardingContent(state = state)
|
||||||
state = state,
|
|
||||||
onOpenDeveloperSettings = onOpenDeveloperSettings
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
footer = {
|
footer = {
|
||||||
OnBoardingButtons(
|
OnBoardingButtons(
|
||||||
|
|
@ -81,10 +73,7 @@ fun OnBoardingView(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OnBoardingContent(
|
private fun OnBoardingContent(state: OnBoardingState) {
|
||||||
state: OnBoardingState,
|
|
||||||
onOpenDeveloperSettings: () -> Unit,
|
|
||||||
) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -127,17 +116,6 @@ private fun OnBoardingContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.isDebugBuild) {
|
|
||||||
IconButton(
|
|
||||||
modifier = Modifier.align(Alignment.TopEnd),
|
|
||||||
onClick = onOpenDeveloperSettings,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = CompoundIcons.SettingsSolid(),
|
|
||||||
contentDescription = stringResource(CommonStrings.common_settings)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,8 +145,8 @@ private fun OnBoardingButtons(
|
||||||
text = stringResource(id = signInButtonStringRes),
|
text = stringResource(id = signInButtonStringRes),
|
||||||
onClick = onSignIn,
|
onClick = onSignIn,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.testTag(TestTags.onBoardingSignIn)
|
.testTag(TestTags.onBoardingSignIn)
|
||||||
)
|
)
|
||||||
if (state.canCreateAccount) {
|
if (state.canCreateAccount) {
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|
@ -181,8 +159,8 @@ private fun OnBoardingButtons(
|
||||||
// Add a report problem text button. Use a Text since we need a special theme here.
|
// Add a report problem text button. Use a Text since we need a special theme here.
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.clickable(onClick = onReportProblem),
|
.clickable(onClick = onReportProblem),
|
||||||
text = stringResource(id = CommonStrings.common_report_a_problem),
|
text = stringResource(id = CommonStrings.common_report_a_problem),
|
||||||
style = ElementTheme.typography.fontBodySmRegular,
|
style = ElementTheme.typography.fontBodySmRegular,
|
||||||
color = ElementTheme.colors.textSecondary,
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
|
@ -200,7 +178,6 @@ internal fun OnBoardingViewPreview(
|
||||||
onSignInWithQrCode = {},
|
onSignInWithQrCode = {},
|
||||||
onSignIn = {},
|
onSignIn = {},
|
||||||
onCreateAccount = {},
|
onCreateAccount = {},
|
||||||
onOpenDeveloperSettings = {},
|
|
||||||
onReportProblem = {},
|
onReportProblem = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import app.cash.molecule.moleculeFlow
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.appconfig.OnBoardingConfig
|
import io.element.android.appconfig.OnBoardingConfig
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||||
|
|
@ -44,27 +43,10 @@ class OnBoardingPresenterTest {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.isDebugBuild).isTrue()
|
|
||||||
assertThat(initialState.canLoginWithQrCode).isFalse()
|
assertThat(initialState.canLoginWithQrCode).isFalse()
|
||||||
assertThat(initialState.productionApplicationName).isEqualTo("B")
|
assertThat(initialState.productionApplicationName).isEqualTo("B")
|
||||||
assertThat(initialState.canCreateAccount).isEqualTo(OnBoardingConfig.CAN_CREATE_ACCOUNT)
|
assertThat(initialState.canCreateAccount).isEqualTo(OnBoardingConfig.CAN_CREATE_ACCOUNT)
|
||||||
|
|
||||||
assertThat(awaitItem().canLoginWithQrCode).isTrue()
|
assertThat(awaitItem().canLoginWithQrCode).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - initial state release`() = runTest {
|
|
||||||
val presenter = OnBoardingPresenter(
|
|
||||||
buildMeta = aBuildMeta(buildType = BuildType.RELEASE),
|
|
||||||
featureFlagService = FakeFeatureFlagService(),
|
|
||||||
)
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
assertThat(initialState.isDebugBuild).isFalse()
|
|
||||||
cancelAndIgnoreRemainingEvents()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,8 @@
|
||||||
package io.element.android.features.onboarding.impl
|
package io.element.android.features.onboarding.impl
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.compose.ui.test.hasContentDescription
|
|
||||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
import androidx.compose.ui.test.performClick
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||||
|
|
@ -74,17 +72,6 @@ class OnboardingViewTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `when on debug build - clicking on the settings icon opens the developer settings`() {
|
|
||||||
ensureCalledOnce { callback ->
|
|
||||||
rule.setOnboardingView(
|
|
||||||
state = anOnBoardingState(isDebugBuild = true),
|
|
||||||
onOpenDeveloperSettings = callback
|
|
||||||
)
|
|
||||||
rule.onNode(hasContentDescription(rule.activity.getString(CommonStrings.common_settings))).performClick()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `clicking on report a problem calls the sign in callback`() {
|
fun `clicking on report a problem calls the sign in callback`() {
|
||||||
ensureCalledOnce { callback ->
|
ensureCalledOnce { callback ->
|
||||||
|
|
@ -101,7 +88,6 @@ class OnboardingViewTest {
|
||||||
onSignInWithQrCode: () -> Unit = EnsureNeverCalled(),
|
onSignInWithQrCode: () -> Unit = EnsureNeverCalled(),
|
||||||
onSignIn: () -> Unit = EnsureNeverCalled(),
|
onSignIn: () -> Unit = EnsureNeverCalled(),
|
||||||
onCreateAccount: () -> Unit = EnsureNeverCalled(),
|
onCreateAccount: () -> Unit = EnsureNeverCalled(),
|
||||||
onOpenDeveloperSettings: () -> Unit = EnsureNeverCalled(),
|
|
||||||
onReportProblem: () -> Unit = EnsureNeverCalled(),
|
onReportProblem: () -> Unit = EnsureNeverCalled(),
|
||||||
) {
|
) {
|
||||||
setContent {
|
setContent {
|
||||||
|
|
@ -110,7 +96,6 @@ class OnboardingViewTest {
|
||||||
onSignInWithQrCode = onSignInWithQrCode,
|
onSignInWithQrCode = onSignInWithQrCode,
|
||||||
onSignIn = onSignIn,
|
onSignIn = onSignIn,
|
||||||
onCreateAccount = onCreateAccount,
|
onCreateAccount = onCreateAccount,
|
||||||
onOpenDeveloperSettings = onOpenDeveloperSettings,
|
|
||||||
onReportProblem = onReportProblem,
|
onReportProblem = onReportProblem,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.preferences.api
|
|
||||||
|
|
||||||
import io.element.android.libraries.architecture.SimpleFeatureEntryPoint
|
|
||||||
|
|
||||||
interface ConfigureTracingEntryPoint : SimpleFeatureEntryPoint
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.preferences.impl
|
|
||||||
|
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
|
||||||
import com.bumble.appyx.core.node.Node
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
|
||||||
import io.element.android.features.preferences.api.ConfigureTracingEntryPoint
|
|
||||||
import io.element.android.features.preferences.impl.developer.tracing.ConfigureTracingNode
|
|
||||||
import io.element.android.libraries.architecture.createNode
|
|
||||||
import io.element.android.libraries.di.AppScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
|
||||||
class DefaultConfigureTracingEntryPoint @Inject constructor() : ConfigureTracingEntryPoint {
|
|
||||||
override fun createNode(parentNode: Node, buildContext: BuildContext): Node {
|
|
||||||
return parentNode.createNode<ConfigureTracingNode>(buildContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -30,7 +30,6 @@ import io.element.android.features.preferences.impl.advanced.AdvancedSettingsNod
|
||||||
import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode
|
import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode
|
||||||
import io.element.android.features.preferences.impl.blockedusers.BlockedUsersNode
|
import io.element.android.features.preferences.impl.blockedusers.BlockedUsersNode
|
||||||
import io.element.android.features.preferences.impl.developer.DeveloperSettingsNode
|
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.notifications.NotificationSettingsNode
|
import io.element.android.features.preferences.impl.notifications.NotificationSettingsNode
|
||||||
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode
|
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode
|
||||||
import io.element.android.features.preferences.impl.root.PreferencesRootNode
|
import io.element.android.features.preferences.impl.root.PreferencesRootNode
|
||||||
|
|
@ -72,9 +71,6 @@ class PreferencesFlowNode @AssistedInject constructor(
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data object AdvancedSettings : NavTarget
|
data object AdvancedSettings : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data object ConfigureTracing : NavTarget
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data object AnalyticsSettings : NavTarget
|
data object AnalyticsSettings : NavTarget
|
||||||
|
|
||||||
|
|
@ -164,15 +160,7 @@ class PreferencesFlowNode @AssistedInject constructor(
|
||||||
createNode<PreferencesRootNode>(buildContext, plugins = listOf(callback))
|
createNode<PreferencesRootNode>(buildContext, plugins = listOf(callback))
|
||||||
}
|
}
|
||||||
NavTarget.DeveloperSettings -> {
|
NavTarget.DeveloperSettings -> {
|
||||||
val callback = object : DeveloperSettingsNode.Callback {
|
createNode<DeveloperSettingsNode>(buildContext)
|
||||||
override fun openConfigureTracing() {
|
|
||||||
backstack.push(NavTarget.ConfigureTracing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createNode<DeveloperSettingsNode>(buildContext, listOf(callback))
|
|
||||||
}
|
|
||||||
NavTarget.ConfigureTracing -> {
|
|
||||||
createNode<ConfigureTracingNode>(buildContext)
|
|
||||||
}
|
}
|
||||||
NavTarget.About -> {
|
NavTarget.About -> {
|
||||||
val callback = object : AboutNode.Callback {
|
val callback = object : AboutNode.Callback {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.features.preferences.impl.developer
|
package io.element.android.features.preferences.impl.developer
|
||||||
|
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
|
||||||
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||||
|
|
||||||
sealed interface DeveloperSettingsEvents {
|
sealed interface DeveloperSettingsEvents {
|
||||||
|
|
@ -14,5 +15,6 @@ sealed interface DeveloperSettingsEvents {
|
||||||
data class SetCustomElementCallBaseUrl(val baseUrl: String?) : DeveloperSettingsEvents
|
data class SetCustomElementCallBaseUrl(val baseUrl: String?) : DeveloperSettingsEvents
|
||||||
data class SetSimplifiedSlidingSyncEnabled(val isEnabled: Boolean) : DeveloperSettingsEvents
|
data class SetSimplifiedSlidingSyncEnabled(val isEnabled: Boolean) : DeveloperSettingsEvents
|
||||||
data class SetHideImagesAndVideos(val value: Boolean) : DeveloperSettingsEvents
|
data class SetHideImagesAndVideos(val value: Boolean) : DeveloperSettingsEvents
|
||||||
|
data class SetTracingLogLevel(val logLevel: LogLevelItem) : DeveloperSettingsEvents
|
||||||
data object ClearCache : DeveloperSettingsEvents
|
data object ClearCache : DeveloperSettingsEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import com.airbnb.android.showkase.models.Showkase
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.bumble.appyx.core.plugin.plugins
|
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
|
@ -28,14 +27,6 @@ class DeveloperSettingsNode @AssistedInject constructor(
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
private val presenter: DeveloperSettingsPresenter,
|
private val presenter: DeveloperSettingsPresenter,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
|
||||||
fun openConfigureTracing()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onOpenConfigureTracing() {
|
|
||||||
plugins<Callback>().forEach { it.openConfigureTracing() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val activity = LocalContext.current as Activity
|
val activity = LocalContext.current as Activity
|
||||||
|
|
@ -49,7 +40,6 @@ class DeveloperSettingsNode @AssistedInject constructor(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onOpenShowkase = ::openShowkase,
|
onOpenShowkase = ::openShowkase,
|
||||||
onOpenConfigureTracing = ::onOpenConfigureTracing,
|
|
||||||
onBackClick = ::navigateUp
|
onBackClick = ::navigateUp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||||
import io.element.android.appconfig.ElementCallConfig
|
import io.element.android.appconfig.ElementCallConfig
|
||||||
import io.element.android.features.logout.api.LogoutUseCase
|
import io.element.android.features.logout.api.LogoutUseCase
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.toLogLevel
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.toLogLevelItem
|
||||||
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
||||||
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
||||||
|
|
@ -37,6 +39,7 @@ import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -76,6 +79,11 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||||
.doesHideImagesAndVideosFlow()
|
.doesHideImagesAndVideosFlow()
|
||||||
.collectAsState(initial = false)
|
.collectAsState(initial = false)
|
||||||
|
|
||||||
|
val tracingLogLevel by appPreferencesStore
|
||||||
|
.getTracingLogLevelFlow()
|
||||||
|
.map { AsyncData.Success(it.toLogLevelItem()) }
|
||||||
|
.collectAsState(initial = AsyncData.Uninitialized)
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
FeatureFlags.entries
|
FeatureFlags.entries
|
||||||
.filter { it.isFinished.not() }
|
.filter { it.isFinished.not() }
|
||||||
|
|
@ -123,6 +131,9 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||||
is DeveloperSettingsEvents.SetHideImagesAndVideos -> coroutineScope.launch {
|
is DeveloperSettingsEvents.SetHideImagesAndVideos -> coroutineScope.launch {
|
||||||
appPreferencesStore.setHideImagesAndVideos(event.value)
|
appPreferencesStore.setHideImagesAndVideos(event.value)
|
||||||
}
|
}
|
||||||
|
is DeveloperSettingsEvents.SetTracingLogLevel -> coroutineScope.launch {
|
||||||
|
appPreferencesStore.setTracingLogLevel(event.logLevel.toLogLevel())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,6 +149,7 @@ class DeveloperSettingsPresenter @Inject constructor(
|
||||||
),
|
),
|
||||||
isSimpleSlidingSyncEnabled = isSimplifiedSlidingSyncEnabled,
|
isSimpleSlidingSyncEnabled = isSimplifiedSlidingSyncEnabled,
|
||||||
hideImagesAndVideos = hideImagesAndVideos,
|
hideImagesAndVideos = hideImagesAndVideos,
|
||||||
|
tracingLogLevel = tracingLogLevel,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.features.preferences.impl.developer
|
package io.element.android.features.preferences.impl.developer
|
||||||
|
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
@ -21,6 +22,7 @@ data class DeveloperSettingsState(
|
||||||
val customElementCallBaseUrlState: CustomElementCallBaseUrlState,
|
val customElementCallBaseUrlState: CustomElementCallBaseUrlState,
|
||||||
val isSimpleSlidingSyncEnabled: Boolean,
|
val isSimpleSlidingSyncEnabled: Boolean,
|
||||||
val hideImagesAndVideos: Boolean,
|
val hideImagesAndVideos: Boolean,
|
||||||
|
val tracingLogLevel: AsyncData<LogLevelItem>,
|
||||||
val eventSink: (DeveloperSettingsEvents) -> Unit
|
val eventSink: (DeveloperSettingsEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
package io.element.android.features.preferences.impl.developer
|
package io.element.android.features.preferences.impl.developer
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
|
||||||
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
@ -42,6 +43,7 @@ fun aDeveloperSettingsState(
|
||||||
customElementCallBaseUrlState = customElementCallBaseUrlState,
|
customElementCallBaseUrlState = customElementCallBaseUrlState,
|
||||||
isSimpleSlidingSyncEnabled = isSimplifiedSlidingSyncEnabled,
|
isSimpleSlidingSyncEnabled = isSimplifiedSlidingSyncEnabled,
|
||||||
hideImagesAndVideos = hideImagesAndVideos,
|
hideImagesAndVideos = hideImagesAndVideos,
|
||||||
|
tracingLogLevel = AsyncData.Success(LogLevelItem.INFO),
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,10 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.preferences.impl.R
|
import io.element.android.features.preferences.impl.R
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
|
||||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView
|
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||||
|
import io.element.android.libraries.designsystem.components.preferences.PreferenceDropdown
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||||
|
|
@ -25,12 +27,12 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.featureflag.ui.FeatureListView
|
import io.element.android.libraries.featureflag.ui.FeatureListView
|
||||||
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
import io.element.android.libraries.featureflag.ui.model.FeatureUiModel
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DeveloperSettingsView(
|
fun DeveloperSettingsView(
|
||||||
state: DeveloperSettingsState,
|
state: DeveloperSettingsState,
|
||||||
onOpenShowkase: () -> Unit,
|
onOpenShowkase: () -> Unit,
|
||||||
onOpenConfigureTracing: () -> Unit,
|
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
@ -49,9 +51,14 @@ fun DeveloperSettingsView(
|
||||||
}
|
}
|
||||||
ElementCallCategory(state = state)
|
ElementCallCategory(state = state)
|
||||||
PreferenceCategory(title = "Rust SDK") {
|
PreferenceCategory(title = "Rust SDK") {
|
||||||
PreferenceText(
|
PreferenceDropdown(
|
||||||
title = "Configure tracing",
|
title = "Tracing log level",
|
||||||
onClick = onOpenConfigureTracing,
|
supportingText = "Requires app reboot",
|
||||||
|
selectedOption = state.tracingLogLevel.dataOrNull(),
|
||||||
|
options = LogLevelItem.entries.toPersistentList(),
|
||||||
|
onSelectOption = { logLevel ->
|
||||||
|
state.eventSink(DeveloperSettingsEvents.SetTracingLogLevel(logLevel))
|
||||||
|
}
|
||||||
)
|
)
|
||||||
PreferenceSwitch(
|
PreferenceSwitch(
|
||||||
title = "Enable Simplified Sliding Sync",
|
title = "Enable Simplified Sliding Sync",
|
||||||
|
|
@ -157,7 +164,6 @@ internal fun DeveloperSettingsViewPreview(@PreviewParameter(DeveloperSettingsSta
|
||||||
DeveloperSettingsView(
|
DeveloperSettingsView(
|
||||||
state = state,
|
state = state,
|
||||||
onOpenShowkase = {},
|
onOpenShowkase = {},
|
||||||
onOpenConfigureTracing = {},
|
|
||||||
onBackClick = {}
|
onBackClick = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<Plugin>,
|
|
||||||
private val presenter: ConfigureTracingPresenter,
|
|
||||||
) : Node(buildContext, plugins = plugins) {
|
|
||||||
@Composable
|
|
||||||
override fun View(modifier: Modifier) {
|
|
||||||
val state = presenter.present()
|
|
||||||
ConfigureTracingView(
|
|
||||||
state = state,
|
|
||||||
onBackClick = ::navigateUp,
|
|
||||||
modifier = modifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 kotlinx.collections.immutable.toImmutableMap
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class ConfigureTracingPresenter @Inject constructor(
|
|
||||||
private val tracingConfigurationStore: TracingConfigurationStore,
|
|
||||||
private val targetLogLevelMapBuilder: TargetLogLevelMapBuilder,
|
|
||||||
) : Presenter<ConfigureTracingState> {
|
|
||||||
@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.toImmutableMap(),
|
|
||||||
eventSink = ::handleEvents
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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: ImmutableMap<Target, LogLevel>,
|
|
||||||
val eventSink: (ConfigureTracingEvents) -> Unit
|
|
||||||
)
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ConfigureTracingState> {
|
|
||||||
override val values: Sequence<ConfigureTracingState>
|
|
||||||
get() = sequenceOf(
|
|
||||||
aConfigureTracingState(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun aConfigureTracingState() = ConfigureTracingState(
|
|
||||||
targetsToLogLevel = persistentMapOf(
|
|
||||||
Target.ELEMENT to LogLevel.INFO,
|
|
||||||
Target.MATRIX_SDK_FFI to LogLevel.WARN,
|
|
||||||
Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.ERROR,
|
|
||||||
),
|
|
||||||
eventSink = {}
|
|
||||||
)
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.preferences.impl.developer.tracing
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
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.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.draw.rotate
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import io.element.android.compound.theme.ElementTheme
|
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
|
||||||
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.ElementPreview
|
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|
||||||
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.HorizontalDivider
|
|
||||||
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 kotlinx.collections.immutable.ImmutableMap
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun ConfigureTracingView(
|
|
||||||
state: ConfigureTracingState,
|
|
||||||
onBackClick: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
|
||||||
Scaffold(
|
|
||||||
modifier = modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.systemBarsPadding()
|
|
||||||
.imePadding(),
|
|
||||||
contentWindowInsets = WindowInsets.statusBars,
|
|
||||||
topBar = {
|
|
||||||
TopAppBar(
|
|
||||||
navigationIcon = {
|
|
||||||
BackButton(onClick = onBackClick)
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(
|
|
||||||
text = "Configure tracing",
|
|
||||||
style = ElementTheme.typography.aliasScreenTitle,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
IconButton(
|
|
||||||
onClick = { showMenu = !showMenu }
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = CompoundIcons.OverflowVertical(),
|
|
||||||
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(
|
|
||||||
imageVector = CompoundIcons.Delete(),
|
|
||||||
tint = ElementTheme.materialColors.secondary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
content = {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(it)
|
|
||||||
.consumeWindowInsets(it)
|
|
||||||
.verticalScroll(state = rememberScrollState())
|
|
||||||
) {
|
|
||||||
ListItem(
|
|
||||||
headlineContent = {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.clickable { Runtime.getRuntime().exit(0) },
|
|
||||||
text = "Tap here to kill the app and apply the changes. You'll have to re-open the app manually.",
|
|
||||||
style = ElementTheme.typography.fontHeadingSmMedium,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
HorizontalDivider()
|
|
||||||
CrateListContent(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun CrateListContent(
|
|
||||||
state: ConfigureTracingState,
|
|
||||||
) {
|
|
||||||
fun onLogLevelChange(target: Target, logLevel: LogLevel) {
|
|
||||||
state.eventSink(ConfigureTracingEvents.UpdateFilter(target, logLevel))
|
|
||||||
}
|
|
||||||
|
|
||||||
TargetAndLogLevelListView(
|
|
||||||
data = state.targetsToLogLevel,
|
|
||||||
onLogLevelChange = ::onLogLevelChange,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TargetAndLogLevelListView(
|
|
||||||
data: ImmutableMap<Target, LogLevel>,
|
|
||||||
onLogLevelChange: (Target, LogLevel) -> Unit,
|
|
||||||
) {
|
|
||||||
Column {
|
|
||||||
data.forEach { item ->
|
|
||||||
fun onLogLevelChange(logLevel: LogLevel) {
|
|
||||||
onLogLevelChange(item.key, logLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
TargetAndLogLevelView(
|
|
||||||
target = item.key,
|
|
||||||
logLevel = item.value,
|
|
||||||
onLogLevelChange = ::onLogLevelChange
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TargetAndLogLevelView(
|
|
||||||
target: Target,
|
|
||||||
logLevel: LogLevel,
|
|
||||||
onLogLevelChange: (LogLevel) -> Unit,
|
|
||||||
) {
|
|
||||||
ListItem(
|
|
||||||
headlineContent = { Text(text = target.filter.takeIf { it.isNotEmpty() } ?: "(common)") },
|
|
||||||
trailingContent = ListItemContent.Custom {
|
|
||||||
LogLevelDropdownMenu(
|
|
||||||
logLevel = logLevel,
|
|
||||||
onLogLevelChange = onLogLevelChange,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun LogLevelDropdownMenu(
|
|
||||||
logLevel: LogLevel,
|
|
||||||
onLogLevelChange: (LogLevel) -> Unit,
|
|
||||||
) {
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
Box {
|
|
||||||
DropdownMenuItem(
|
|
||||||
modifier = Modifier.widthIn(max = 120.dp),
|
|
||||||
text = { Text(text = logLevel.filter) },
|
|
||||||
onClick = { expanded = !expanded },
|
|
||||||
trailingIcon = {
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier.rotate(if (expanded) 180f else 0f),
|
|
||||||
imageVector = CompoundIcons.ChevronDown(),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
DropdownMenu(
|
|
||||||
expanded = expanded,
|
|
||||||
onDismissRequest = { expanded = false },
|
|
||||||
) {
|
|
||||||
LogLevel.values().forEach { logLevel ->
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = {
|
|
||||||
Text(text = logLevel.filter)
|
|
||||||
},
|
|
||||||
onClick = {
|
|
||||||
expanded = false
|
|
||||||
onLogLevelChange(logLevel)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun ConfigureTracingViewPreview(
|
|
||||||
@PreviewParameter(ConfigureTracingStateProvider::class) state: ConfigureTracingState
|
|
||||||
) = ElementPreview {
|
|
||||||
ConfigureTracingView(
|
|
||||||
state = state,
|
|
||||||
onBackClick = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.preferences.impl.developer.tracing
|
||||||
|
|
||||||
|
import io.element.android.libraries.designsystem.components.preferences.DropdownOption
|
||||||
|
|
||||||
|
enum class LogLevelItem : DropdownOption {
|
||||||
|
ERROR {
|
||||||
|
override val text: String = "Error"
|
||||||
|
},
|
||||||
|
WARN {
|
||||||
|
override val text: String = "Warn"
|
||||||
|
},
|
||||||
|
INFO {
|
||||||
|
override val text: String = "Info"
|
||||||
|
},
|
||||||
|
DEBUG {
|
||||||
|
override val text: String = "Debug"
|
||||||
|
},
|
||||||
|
TRACE {
|
||||||
|
override val text: String = "Trace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.preferences.impl.developer.tracing
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||||
|
|
||||||
|
fun LogLevelItem.toLogLevel(): LogLevel {
|
||||||
|
return when (this) {
|
||||||
|
LogLevelItem.ERROR -> io.element.android.libraries.matrix.api.tracing.LogLevel.ERROR
|
||||||
|
LogLevelItem.WARN -> io.element.android.libraries.matrix.api.tracing.LogLevel.WARN
|
||||||
|
LogLevelItem.INFO -> io.element.android.libraries.matrix.api.tracing.LogLevel.INFO
|
||||||
|
LogLevelItem.DEBUG -> io.element.android.libraries.matrix.api.tracing.LogLevel.DEBUG
|
||||||
|
LogLevelItem.TRACE -> io.element.android.libraries.matrix.api.tracing.LogLevel.TRACE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LogLevel.toLogLevelItem(): LogLevelItem {
|
||||||
|
return when (this) {
|
||||||
|
LogLevel.ERROR -> LogLevelItem.ERROR
|
||||||
|
LogLevel.WARN -> LogLevelItem.WARN
|
||||||
|
LogLevel.INFO -> LogLevelItem.INFO
|
||||||
|
LogLevel.DEBUG -> LogLevelItem.DEBUG
|
||||||
|
LogLevel.TRACE -> LogLevelItem.TRACE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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.TracingFilterConfiguration
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class TargetLogLevelMapBuilder @Inject constructor(
|
|
||||||
private val tracingConfigurationStore: TracingConfigurationStore,
|
|
||||||
private val defaultConfig: TracingFilterConfiguration,
|
|
||||||
) {
|
|
||||||
fun getDefaultMap(): Map<Target, LogLevel> {
|
|
||||||
return Target.entries.associateWith { target ->
|
|
||||||
defaultConfig.getLogLevel(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCurrentMap(): Map<Target, LogLevel> {
|
|
||||||
return Target.entries.associateWith { target ->
|
|
||||||
tracingConfigurationStore.getLogLevel(target)
|
|
||||||
?: defaultConfig.getLogLevel(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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.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 SharedPreferencesTracingConfigurationStore @Inject constructor(
|
|
||||||
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_"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,6 +12,7 @@ package io.element.android.features.preferences.impl.developer
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.appconfig.ElementCallConfig
|
import io.element.android.appconfig.ElementCallConfig
|
||||||
import io.element.android.features.logout.test.FakeLogoutUseCase
|
import io.element.android.features.logout.test.FakeLogoutUseCase
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
|
||||||
import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase
|
import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase
|
||||||
import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase
|
import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase
|
||||||
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
||||||
|
|
@ -52,11 +53,13 @@ class DeveloperSettingsPresenterTest {
|
||||||
assertThat(state.rageshakeState.isEnabled).isFalse()
|
assertThat(state.rageshakeState.isEnabled).isFalse()
|
||||||
assertThat(state.rageshakeState.isSupported).isTrue()
|
assertThat(state.rageshakeState.isSupported).isTrue()
|
||||||
assertThat(state.rageshakeState.sensitivity).isEqualTo(0.3f)
|
assertThat(state.rageshakeState.sensitivity).isEqualTo(0.3f)
|
||||||
|
assertThat(state.tracingLogLevel).isEqualTo(AsyncData.Uninitialized)
|
||||||
}
|
}
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state.features).isNotEmpty()
|
assertThat(state.features).isNotEmpty()
|
||||||
val numberOfModifiableFeatureFlags = FeatureFlags.entries.count { it.isFinished.not() }
|
val numberOfModifiableFeatureFlags = FeatureFlags.entries.count { it.isFinished.not() }
|
||||||
assertThat(state.features).hasSize(numberOfModifiableFeatureFlags)
|
assertThat(state.features).hasSize(numberOfModifiableFeatureFlags)
|
||||||
|
assertThat(state.tracingLogLevel.dataOrNull()).isEqualTo(LogLevelItem.INFO)
|
||||||
}
|
}
|
||||||
awaitItem().also { state ->
|
awaitItem().also { state ->
|
||||||
assertThat(state.cacheSize).isInstanceOf(AsyncData.Success::class.java)
|
assertThat(state.cacheSize).isInstanceOf(AsyncData.Success::class.java)
|
||||||
|
|
@ -200,6 +203,22 @@ class DeveloperSettingsPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - changing tracing log level`() = runTest {
|
||||||
|
val preferences = InMemoryAppPreferencesStore()
|
||||||
|
val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences)
|
||||||
|
presenter.test {
|
||||||
|
skipItems(2)
|
||||||
|
awaitItem().also { state ->
|
||||||
|
assertThat(state.tracingLogLevel.dataOrNull()).isEqualTo(LogLevelItem.INFO)
|
||||||
|
state.eventSink(DeveloperSettingsEvents.SetTracingLogLevel(LogLevelItem.TRACE))
|
||||||
|
}
|
||||||
|
awaitItem().also { state ->
|
||||||
|
assertThat(state.tracingLogLevel.dataOrNull()).isEqualTo(LogLevelItem.TRACE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createDeveloperSettingsPresenter(
|
private fun createDeveloperSettingsPresenter(
|
||||||
featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(),
|
featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(),
|
||||||
cacheSizeUseCase: FakeComputeCacheSizeUseCase = FakeComputeCacheSizeUseCase(),
|
cacheSizeUseCase: FakeComputeCacheSizeUseCase = FakeComputeCacheSizeUseCase(),
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import androidx.compose.ui.test.onNodeWithText
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import io.element.android.features.preferences.impl.R
|
import io.element.android.features.preferences.impl.R
|
||||||
|
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||||
import io.element.android.tests.testutils.EventsRecorder
|
import io.element.android.tests.testutils.EventsRecorder
|
||||||
|
|
@ -76,17 +77,16 @@ class DeveloperSettingsViewTest {
|
||||||
|
|
||||||
@Config(qualifiers = "h1024dp")
|
@Config(qualifiers = "h1024dp")
|
||||||
@Test
|
@Test
|
||||||
fun `clicking on configure tracing invokes the expected callback`() {
|
fun `clicking on log level emits the expected event`() {
|
||||||
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>(expectEvents = false)
|
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>()
|
||||||
ensureCalledOnce {
|
rule.setDeveloperSettingsView(
|
||||||
rule.setDeveloperSettingsView(
|
state = aDeveloperSettingsState(
|
||||||
state = aDeveloperSettingsState(
|
eventSink = eventsRecorder
|
||||||
eventSink = eventsRecorder
|
),
|
||||||
),
|
)
|
||||||
onOpenConfigureTracing = it
|
rule.onNodeWithText("Tracing log level").performClick()
|
||||||
)
|
rule.onNodeWithText("Debug").performClick()
|
||||||
rule.onNodeWithText("Configure tracing").performClick()
|
eventsRecorder.assertSingle(DeveloperSettingsEvents.SetTracingLogLevel(LogLevelItem.DEBUG))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Config(qualifiers = "h1500dp")
|
@Config(qualifiers = "h1500dp")
|
||||||
|
|
@ -131,14 +131,12 @@ class DeveloperSettingsViewTest {
|
||||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setDeveloperSettingsView(
|
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setDeveloperSettingsView(
|
||||||
state: DeveloperSettingsState,
|
state: DeveloperSettingsState,
|
||||||
onOpenShowkase: () -> Unit = EnsureNeverCalled(),
|
onOpenShowkase: () -> Unit = EnsureNeverCalled(),
|
||||||
onOpenConfigureTracing: () -> Unit = EnsureNeverCalled(),
|
|
||||||
onBackClick: () -> Unit = EnsureNeverCalled()
|
onBackClick: () -> Unit = EnsureNeverCalled()
|
||||||
) {
|
) {
|
||||||
setContent {
|
setContent {
|
||||||
DeveloperSettingsView(
|
DeveloperSettingsView(
|
||||||
state = state,
|
state = state,
|
||||||
onOpenShowkase = onOpenShowkase,
|
onOpenShowkase = onOpenShowkase,
|
||||||
onOpenConfigureTracing = onOpenConfigureTracing,
|
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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.libraries.matrix.api.tracing.TracingFilterConfigurations
|
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
|
||||||
import io.element.android.tests.testutils.waitForPredicate
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class ConfigureTracingPresenterTest {
|
|
||||||
@get:Rule
|
|
||||||
val warmUpRule = WarmUpRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - initial state`() = runTest {
|
|
||||||
val store = InMemoryTracingConfigurationStore()
|
|
||||||
val presenter = ConfigureTracingPresenter(
|
|
||||||
store,
|
|
||||||
TargetLogLevelMapBuilder(store, TracingFilterConfigurations.debug),
|
|
||||||
)
|
|
||||||
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, TracingFilterConfigurations.debug),
|
|
||||||
)
|
|
||||||
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, TracingFilterConfigurations.debug),
|
|
||||||
)
|
|
||||||
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, TracingFilterConfigurations.debug),
|
|
||||||
)
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023, 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -35,9 +35,9 @@ interface BugReporter {
|
||||||
fun logDirectory(): File
|
fun logDirectory(): File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current tracing filter.
|
* Set the current tracing log level.
|
||||||
*/
|
*/
|
||||||
fun setCurrentTracingFilter(tracingFilter: String)
|
fun setCurrentTracingLogLevel(logLevel: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the logcat.
|
* Save the logcat.
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class DefaultBugReporter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val logcatCommandDebug = arrayOf("logcat", "-d", "-v", "threadtime", "*:*")
|
private val logcatCommandDebug = arrayOf("logcat", "-d", "-v", "threadtime", "*:*")
|
||||||
private var currentTracingFilter: String? = null
|
private var currentTracingLogLevel: String? = null
|
||||||
|
|
||||||
private val logCatErrFile = File(logDirectory().absolutePath, LOG_CAT_FILENAME)
|
private val logCatErrFile = File(logDirectory().absolutePath, LOG_CAT_FILENAME)
|
||||||
|
|
||||||
|
|
@ -156,8 +156,8 @@ class DefaultBugReporter @Inject constructor(
|
||||||
if (crashCallStack.isNotEmpty() && withCrashLogs) {
|
if (crashCallStack.isNotEmpty() && withCrashLogs) {
|
||||||
builder.addFormDataPart("label", "crash")
|
builder.addFormDataPart("label", "crash")
|
||||||
}
|
}
|
||||||
currentTracingFilter?.let {
|
currentTracingLogLevel?.let {
|
||||||
builder.addFormDataPart("tracing_filter", it)
|
builder.addFormDataPart("tracing_log_level", it)
|
||||||
}
|
}
|
||||||
if (buildMeta.isEnterpriseBuild) {
|
if (buildMeta.isEnterpriseBuild) {
|
||||||
builder.addFormDataPart("label", "Enterprise")
|
builder.addFormDataPart("label", "Enterprise")
|
||||||
|
|
@ -299,8 +299,8 @@ class DefaultBugReporter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setCurrentTracingFilter(tracingFilter: String) {
|
override fun setCurrentTracingLogLevel(logLevel: String) {
|
||||||
currentTracingFilter = tracingFilter
|
currentTracingLogLevel = logLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class FakeBugReporter(val mode: Mode = Mode.Success) : BugReporter {
|
||||||
return File("fake")
|
return File("fake")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setCurrentTracingFilter(tracingFilter: String) {
|
override fun setCurrentTracingLogLevel(logLevel: String) {
|
||||||
// No op
|
// No op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ jsoup = "org.jsoup:jsoup:1.18.3"
|
||||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||||
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
||||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.73"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.75"
|
||||||
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
||||||
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
||||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|
||||||
|
package io.element.android.libraries.designsystem.components.preferences
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
|
import androidx.compose.material3.MenuAnchorType
|
||||||
|
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.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
|
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||||
|
import io.element.android.libraries.designsystem.components.preferences.components.preferenceIcon
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||||
|
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.designsystem.toEnabledColor
|
||||||
|
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T : DropdownOption> PreferenceDropdown(
|
||||||
|
title: String,
|
||||||
|
selectedOption: T?,
|
||||||
|
options: ImmutableList<T>,
|
||||||
|
onSelectOption: (T) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
supportingText: String? = null,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
icon: ImageVector? = null,
|
||||||
|
@DrawableRes iconResourceId: Int? = null,
|
||||||
|
showIconAreaIfNoIcon: Boolean = false,
|
||||||
|
) {
|
||||||
|
var isDropdownExpanded by remember { mutableStateOf(false) }
|
||||||
|
ListItem(
|
||||||
|
modifier = modifier,
|
||||||
|
leadingContent = preferenceIcon(
|
||||||
|
icon = icon,
|
||||||
|
iconResourceId = iconResourceId,
|
||||||
|
enabled = enabled,
|
||||||
|
showIconAreaIfNoIcon = showIconAreaIfNoIcon,
|
||||||
|
),
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
style = ElementTheme.typography.fontBodyLgRegular,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
text = title,
|
||||||
|
color = enabled.toEnabledColor(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingContent = supportingText?.let {
|
||||||
|
{
|
||||||
|
Text(
|
||||||
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
|
text = it,
|
||||||
|
color = enabled.toSecondaryEnabledColor(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trailingContent = ListItemContent.Custom(
|
||||||
|
content = {
|
||||||
|
DropdownTrailingContent(
|
||||||
|
selectedOption = selectedOption,
|
||||||
|
options = options,
|
||||||
|
onSelectOption = onSelectOption,
|
||||||
|
expanded = isDropdownExpanded,
|
||||||
|
onExpandedChange = { isDropdownExpanded = it },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
onClick = { isDropdownExpanded = true }.takeIf { !isDropdownExpanded },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dropdown option that can be used in a [PreferenceDropdown].
|
||||||
|
*/
|
||||||
|
interface DropdownOption {
|
||||||
|
/**
|
||||||
|
* The text to display for this option.
|
||||||
|
*/
|
||||||
|
val text: String
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun <T : DropdownOption> DropdownTrailingContent(
|
||||||
|
selectedOption: T?,
|
||||||
|
options: ImmutableList<T>,
|
||||||
|
expanded: Boolean,
|
||||||
|
onExpandedChange: (Boolean) -> Unit,
|
||||||
|
onSelectOption: (T) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
ExposedDropdownMenuBox(
|
||||||
|
expanded = expanded,
|
||||||
|
onExpandedChange = onExpandedChange,
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = selectedOption?.text.orEmpty(),
|
||||||
|
maxLines = 1,
|
||||||
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
)
|
||||||
|
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
|
||||||
|
}
|
||||||
|
ExposedDropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { onExpandedChange(false) },
|
||||||
|
matchTextFieldWidth = false,
|
||||||
|
) {
|
||||||
|
options.forEach { option ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = option.text,
|
||||||
|
style = ElementTheme.typography.fontBodyMdRegular
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
onSelectOption(option)
|
||||||
|
onExpandedChange(false)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(group = PreviewGroup.Preferences)
|
||||||
|
@Composable
|
||||||
|
internal fun PreferenceDropdownPreview() = ElementThemedPreview {
|
||||||
|
val options = listOf(
|
||||||
|
object : DropdownOption {
|
||||||
|
override val text = "Option 1"
|
||||||
|
},
|
||||||
|
object : DropdownOption {
|
||||||
|
override val text = "Option 2"
|
||||||
|
},
|
||||||
|
object : DropdownOption {
|
||||||
|
override val text = "Option 3"
|
||||||
|
},
|
||||||
|
).toImmutableList()
|
||||||
|
|
||||||
|
Column {
|
||||||
|
PreferenceDropdown(
|
||||||
|
title = "Dropdown",
|
||||||
|
supportingText = "Options for dropdown",
|
||||||
|
icon = CompoundIcons.Threads(),
|
||||||
|
selectedOption = null,
|
||||||
|
options = options,
|
||||||
|
onSelectOption = {},
|
||||||
|
)
|
||||||
|
PreferenceDropdown(
|
||||||
|
title = "Dropdown",
|
||||||
|
supportingText = "Options for dropdown",
|
||||||
|
icon = CompoundIcons.Threads(),
|
||||||
|
selectedOption = options.first(),
|
||||||
|
options = options,
|
||||||
|
onSelectOption = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022-2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.api.tracing
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log levels for tracing in the SDK.
|
||||||
|
*/
|
||||||
|
enum class LogLevel {
|
||||||
|
ERROR,
|
||||||
|
WARN,
|
||||||
|
INFO,
|
||||||
|
DEBUG,
|
||||||
|
TRACE,
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
package io.element.android.libraries.matrix.api.tracing
|
package io.element.android.libraries.matrix.api.tracing
|
||||||
|
|
||||||
data class TracingConfiguration(
|
data class TracingConfiguration(
|
||||||
val filterConfiguration: TracingFilterConfiguration,
|
val logLevel: LogLevel,
|
||||||
|
val extraTargets: List<String>,
|
||||||
val writesToLogcat: Boolean,
|
val writesToLogcat: Boolean,
|
||||||
val writesToFilesConfiguration: WriteToFilesConfiguration,
|
val writesToFilesConfiguration: WriteToFilesConfiguration,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2022-2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.api.tracing
|
|
||||||
|
|
||||||
data class TracingFilterConfiguration(
|
|
||||||
val overrides: Map<Target, LogLevel> = emptyMap(),
|
|
||||||
) {
|
|
||||||
private val defaultLogLevel = LogLevel.INFO
|
|
||||||
|
|
||||||
// Order should matters
|
|
||||||
private val targetsToLogLevel: Map<Target, LogLevel> = mapOf(
|
|
||||||
Target.HYPER to LogLevel.WARN,
|
|
||||||
Target.MATRIX_SDK_CRYPTO to LogLevel.DEBUG,
|
|
||||||
Target.MATRIX_SDK_CRYPTO_ACCOUNT to LogLevel.TRACE,
|
|
||||||
Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.DEBUG,
|
|
||||||
Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.INFO,
|
|
||||||
Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.INFO,
|
|
||||||
Target.MATRIX_SDK_UI_TIMELINE to LogLevel.INFO,
|
|
||||||
Target.MATRIX_SDK_BASE_CLIENT to LogLevel.TRACE,
|
|
||||||
// To debug OIDC logouts
|
|
||||||
Target.MATRIX_SDK_OIDC to LogLevel.TRACE,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getLogLevel(target: Target): LogLevel {
|
|
||||||
return overrides[target] ?: targetsToLogLevel[target] ?: defaultLogLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
val filter: String
|
|
||||||
get() {
|
|
||||||
val fullMap = Target.entries.associateWith {
|
|
||||||
overrides[it] ?: targetsToLogLevel[it] ?: defaultLogLevel
|
|
||||||
}
|
|
||||||
return fullMap.map {
|
|
||||||
if (it.key.filter.isEmpty()) {
|
|
||||||
it.value.filter
|
|
||||||
} else {
|
|
||||||
"${it.key.filter}=${it.value.filter}"
|
|
||||||
}
|
|
||||||
}.joinToString(separator = ",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Target(open val filter: String) {
|
|
||||||
// COMMON(""),
|
|
||||||
ELEMENT("elementx"),
|
|
||||||
HYPER("hyper"),
|
|
||||||
MATRIX_SDK_FFI("matrix_sdk_ffi"),
|
|
||||||
MATRIX_SDK_UNIFFI_API("matrix_sdk_ffi::uniffi_api"),
|
|
||||||
MATRIX_SDK_CRYPTO("matrix_sdk_crypto"),
|
|
||||||
MATRIX_SDK_CRYPTO_ACCOUNT("matrix_sdk_crypto::olm::account"),
|
|
||||||
MATRIX_SDK("matrix_sdk"),
|
|
||||||
MATRIX_SDK_HTTP_CLIENT("matrix_sdk::http_client"),
|
|
||||||
MATRIX_SDK_CLIENT("matrix_sdk::client"),
|
|
||||||
MATRIX_SDK_OIDC("matrix_sdk::oidc"),
|
|
||||||
MATRIX_SDK_SEND_QUEUE("matrix_sdk::send_queue"),
|
|
||||||
MATRIX_SDK_SLIDING_SYNC("matrix_sdk::sliding_sync"),
|
|
||||||
MATRIX_SDK_BASE_SLIDING_SYNC("matrix_sdk_base::sliding_sync"),
|
|
||||||
MATRIX_SDK_UI_TIMELINE("matrix_sdk_ui::timeline"),
|
|
||||||
MATRIX_SDK_BASE_READ_RECEIPTS("matrix_sdk_base::read_receipts"),
|
|
||||||
MATRIX_SDK_BASE_CLIENT("matrix_sdk_base"),
|
|
||||||
}
|
|
||||||
|
|
||||||
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.ELEMENT to LogLevel.DEBUG
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val nightly = TracingFilterConfiguration(
|
|
||||||
overrides = mapOf(
|
|
||||||
Target.ELEMENT to LogLevel.TRACE,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val debug = TracingFilterConfiguration(
|
|
||||||
overrides = mapOf(
|
|
||||||
Target.ELEMENT to LogLevel.TRACE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this method to create a custom configuration where all targets will have the same log level.
|
|
||||||
*/
|
|
||||||
fun custom(logLevel: LogLevel) = TracingFilterConfiguration(overrides = Target.entries.associateWith { logLevel })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this method to override the log level of specific targets.
|
|
||||||
*/
|
|
||||||
fun custom(overrides: Map<Target, LogLevel>) = TracingFilterConfiguration(overrides)
|
|
||||||
}
|
|
||||||
|
|
@ -11,5 +11,5 @@ import timber.log.Timber
|
||||||
|
|
||||||
interface TracingService {
|
interface TracingService {
|
||||||
fun setupTracing(tracingConfiguration: TracingConfiguration)
|
fun setupTracing(tracingConfiguration: TracingConfiguration)
|
||||||
fun createTimberTree(): Timber.Tree
|
fun createTimberTree(target: String): Timber.Tree
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -311,8 +311,8 @@ class RustMatrixClient(
|
||||||
isEncrypted = createRoomParams.isEncrypted,
|
isEncrypted = createRoomParams.isEncrypted,
|
||||||
isDirect = createRoomParams.isDirect,
|
isDirect = createRoomParams.isDirect,
|
||||||
visibility = when (createRoomParams.visibility) {
|
visibility = when (createRoomParams.visibility) {
|
||||||
RoomVisibility.PUBLIC -> RustRoomVisibility.PUBLIC
|
RoomVisibility.PUBLIC -> RustRoomVisibility.Public
|
||||||
RoomVisibility.PRIVATE -> RustRoomVisibility.PRIVATE
|
RoomVisibility.PRIVATE -> RustRoomVisibility.Private
|
||||||
},
|
},
|
||||||
preset = when (createRoomParams.visibility) {
|
preset = when (createRoomParams.visibility) {
|
||||||
RoomVisibility.PRIVATE -> {
|
RoomVisibility.PRIVATE -> {
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.di
|
|
||||||
|
|
||||||
import com.squareup.anvil.annotations.ContributesTo
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.Provides
|
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
|
||||||
import io.element.android.libraries.di.AppScope
|
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingFilterConfiguration
|
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations
|
|
||||||
|
|
||||||
@Module
|
|
||||||
@ContributesTo(AppScope::class)
|
|
||||||
object TracingMatrixModule {
|
|
||||||
@Provides
|
|
||||||
fun providesTracingFilterConfiguration(buildMeta: BuildMeta): TracingFilterConfiguration {
|
|
||||||
return when (buildMeta.buildType) {
|
|
||||||
BuildType.DEBUG -> TracingFilterConfigurations.debug
|
|
||||||
BuildType.NIGHTLY -> TracingFilterConfigurations.nightly
|
|
||||||
BuildType.RELEASE -> TracingFilterConfigurations.release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -28,4 +28,5 @@ fun ImageInfo.map(): RustImageInfo = RustImageInfo(
|
||||||
thumbnailInfo = thumbnailInfo?.map(),
|
thumbnailInfo = thumbnailInfo?.map(),
|
||||||
thumbnailSource = null,
|
thumbnailSource = null,
|
||||||
blurhash = blurhash,
|
blurhash = blurhash,
|
||||||
|
isAnimated = null
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ import org.matrix.rustcomponents.sdk.FormattedBody
|
||||||
import org.matrix.rustcomponents.sdk.MessageFormat
|
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||||
import org.matrix.rustcomponents.sdk.PollData
|
import org.matrix.rustcomponents.sdk.PollData
|
||||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||||
|
import org.matrix.rustcomponents.sdk.UploadParameters
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uniffi.matrix_sdk_ui.LiveBackPaginationStatus
|
import uniffi.matrix_sdk_ui.LiveBackPaginationStatus
|
||||||
|
|
@ -323,6 +324,7 @@ class RustTimeline(
|
||||||
formattedCaption = formattedCaption?.let {
|
formattedCaption = formattedCaption?.let {
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
},
|
},
|
||||||
|
mentions = null,
|
||||||
)
|
)
|
||||||
inner.edit(
|
inner.edit(
|
||||||
newContent = editedContent,
|
newContent = editedContent,
|
||||||
|
|
@ -355,14 +357,17 @@ class RustTimeline(
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
||||||
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
||||||
inner.sendImage(
|
inner.sendImage(
|
||||||
url = file.path,
|
params = UploadParameters(
|
||||||
thumbnailUrl = thumbnailFile?.path,
|
filename = file.path,
|
||||||
|
caption = caption,
|
||||||
|
formattedCaption = formattedCaption?.let {
|
||||||
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
|
},
|
||||||
|
useSendQueue = useSendQueue,
|
||||||
|
mentions = null,
|
||||||
|
),
|
||||||
|
thumbnailPath = thumbnailFile?.path,
|
||||||
imageInfo = imageInfo.map(),
|
imageInfo = imageInfo.map(),
|
||||||
caption = caption,
|
|
||||||
formattedCaption = formattedCaption?.let {
|
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
|
||||||
},
|
|
||||||
useSendQueue = useSendQueue,
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher()
|
progressWatcher = progressCallback?.toProgressWatcher()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -379,14 +384,17 @@ class RustTimeline(
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
||||||
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
|
||||||
inner.sendVideo(
|
inner.sendVideo(
|
||||||
url = file.path,
|
params = UploadParameters(
|
||||||
thumbnailUrl = thumbnailFile?.path,
|
filename = file.path,
|
||||||
|
caption = caption,
|
||||||
|
formattedCaption = formattedCaption?.let {
|
||||||
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
|
},
|
||||||
|
useSendQueue = useSendQueue,
|
||||||
|
mentions = null,
|
||||||
|
),
|
||||||
|
thumbnailPath = thumbnailFile?.path,
|
||||||
videoInfo = videoInfo.map(),
|
videoInfo = videoInfo.map(),
|
||||||
caption = caption,
|
|
||||||
formattedCaption = formattedCaption?.let {
|
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
|
||||||
},
|
|
||||||
useSendQueue = useSendQueue,
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher()
|
progressWatcher = progressCallback?.toProgressWatcher()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -402,13 +410,16 @@ class RustTimeline(
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
||||||
return sendAttachment(listOf(file)) {
|
return sendAttachment(listOf(file)) {
|
||||||
inner.sendAudio(
|
inner.sendAudio(
|
||||||
url = file.path,
|
params = UploadParameters(
|
||||||
|
filename = file.path,
|
||||||
|
caption = caption,
|
||||||
|
formattedCaption = formattedCaption?.let {
|
||||||
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
|
},
|
||||||
|
useSendQueue = useSendQueue,
|
||||||
|
mentions = null,
|
||||||
|
),
|
||||||
audioInfo = audioInfo.map(),
|
audioInfo = audioInfo.map(),
|
||||||
caption = caption,
|
|
||||||
formattedCaption = formattedCaption?.let {
|
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
|
||||||
},
|
|
||||||
useSendQueue = useSendQueue,
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher()
|
progressWatcher = progressCallback?.toProgressWatcher()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -424,13 +435,16 @@ class RustTimeline(
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
||||||
return sendAttachment(listOf(file)) {
|
return sendAttachment(listOf(file)) {
|
||||||
inner.sendFile(
|
inner.sendFile(
|
||||||
url = file.path,
|
params = UploadParameters(
|
||||||
|
filename = file.path,
|
||||||
|
caption = caption,
|
||||||
|
formattedCaption = formattedCaption?.let {
|
||||||
|
FormattedBody(body = it, format = MessageFormat.Html)
|
||||||
|
},
|
||||||
|
useSendQueue = useSendQueue,
|
||||||
|
mentions = null,
|
||||||
|
),
|
||||||
fileInfo = fileInfo.map(),
|
fileInfo = fileInfo.map(),
|
||||||
caption = caption,
|
|
||||||
formattedCaption = formattedCaption?.let {
|
|
||||||
FormattedBody(body = it, format = MessageFormat.Html)
|
|
||||||
},
|
|
||||||
useSendQueue = useSendQueue,
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher(),
|
progressWatcher = progressCallback?.toProgressWatcher(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -543,13 +557,16 @@ class RustTimeline(
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
||||||
return sendAttachment(listOf(file)) {
|
return sendAttachment(listOf(file)) {
|
||||||
inner.sendVoiceMessage(
|
inner.sendVoiceMessage(
|
||||||
url = file.path,
|
params = UploadParameters(
|
||||||
|
filename = file.path,
|
||||||
|
// Maybe allow a caption in the future?
|
||||||
|
caption = null,
|
||||||
|
formattedCaption = null,
|
||||||
|
useSendQueue = useSendQueue,
|
||||||
|
mentions = null,
|
||||||
|
),
|
||||||
audioInfo = audioInfo.map(),
|
audioInfo = audioInfo.map(),
|
||||||
waveform = waveform.toMSC3246range(),
|
waveform = waveform.toMSC3246range(),
|
||||||
// Maybe allow a caption in the future?
|
|
||||||
caption = null,
|
|
||||||
formattedCaption = null,
|
|
||||||
useSendQueue = useSendQueue,
|
|
||||||
progressWatcher = progressCallback?.toProgressWatcher(),
|
progressWatcher = progressCallback?.toProgressWatcher(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.tracing
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.libraries.core.meta.BuildMeta
|
import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
|
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingService
|
import io.element.android.libraries.matrix.api.tracing.TracingService
|
||||||
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
||||||
|
|
@ -20,18 +21,28 @@ import javax.inject.Inject
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) : TracingService {
|
class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) : TracingService {
|
||||||
override fun setupTracing(tracingConfiguration: TracingConfiguration) {
|
override fun setupTracing(tracingConfiguration: TracingConfiguration) {
|
||||||
val filter = tracingConfiguration.filterConfiguration
|
|
||||||
val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration(
|
val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration(
|
||||||
filter = tracingConfiguration.filterConfiguration.filter,
|
|
||||||
writeToStdoutOrSystem = tracingConfiguration.writesToLogcat,
|
writeToStdoutOrSystem = tracingConfiguration.writesToLogcat,
|
||||||
|
logLevel = tracingConfiguration.logLevel.toRustLogLevel(),
|
||||||
|
extraTargets = tracingConfiguration.extraTargets,
|
||||||
writeToFiles = tracingConfiguration.writesToFilesConfiguration.toTracingFileConfiguration(),
|
writeToFiles = tracingConfiguration.writesToFilesConfiguration.toTracingFileConfiguration(),
|
||||||
)
|
)
|
||||||
org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration)
|
org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration)
|
||||||
Timber.v("Tracing config filter = $filter: ${filter.filter}")
|
Timber.d("setupTracing: $rustTracingConfiguration")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createTimberTree(): Timber.Tree {
|
override fun createTimberTree(target: String): Timber.Tree {
|
||||||
return RustTracingTree(retrieveFromStackTrace = buildMeta.isDebuggable)
|
return RustTracingTree(target = target, retrieveFromStackTrace = buildMeta.isDebuggable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LogLevel.toRustLogLevel(): org.matrix.rustcomponents.sdk.LogLevel {
|
||||||
|
return when (this) {
|
||||||
|
LogLevel.ERROR -> org.matrix.rustcomponents.sdk.LogLevel.ERROR
|
||||||
|
LogLevel.WARN -> org.matrix.rustcomponents.sdk.LogLevel.WARN
|
||||||
|
LogLevel.INFO -> org.matrix.rustcomponents.sdk.LogLevel.INFO
|
||||||
|
LogLevel.DEBUG -> org.matrix.rustcomponents.sdk.LogLevel.DEBUG
|
||||||
|
LogLevel.TRACE -> org.matrix.rustcomponents.sdk.LogLevel.TRACE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
package io.element.android.libraries.matrix.impl.tracing
|
package io.element.android.libraries.matrix.impl.tracing
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import io.element.android.libraries.matrix.api.tracing.Target
|
|
||||||
import org.matrix.rustcomponents.sdk.LogLevel
|
import org.matrix.rustcomponents.sdk.LogLevel
|
||||||
import org.matrix.rustcomponents.sdk.logEvent
|
import org.matrix.rustcomponents.sdk.logEvent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
@ -26,7 +25,10 @@ private val fqcnIgnore = listOf(
|
||||||
/**
|
/**
|
||||||
* A Timber tree that passes logs to the Rust SDK.
|
* A Timber tree that passes logs to the Rust SDK.
|
||||||
*/
|
*/
|
||||||
internal class RustTracingTree(private val retrieveFromStackTrace: Boolean) : Timber.Tree() {
|
internal class RustTracingTree(
|
||||||
|
private val target: String,
|
||||||
|
private val retrieveFromStackTrace: Boolean,
|
||||||
|
) : Timber.Tree() {
|
||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||||
val location = if (retrieveFromStackTrace) {
|
val location = if (retrieveFromStackTrace) {
|
||||||
getLogEventLocationFromStackTrace()
|
getLogEventLocationFromStackTrace()
|
||||||
|
|
@ -38,7 +40,7 @@ internal class RustTracingTree(private val retrieveFromStackTrace: Boolean) : Ti
|
||||||
file = location.file,
|
file = location.file,
|
||||||
line = location.line,
|
line = location.line,
|
||||||
level = logLevel,
|
level = logLevel,
|
||||||
target = Target.ELEMENT.filter,
|
target = target,
|
||||||
message = if (tag != null) "[$tag] $message" else message,
|
message = if (tag != null) "[$tag] $message" else message,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
* Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.di
|
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
|
||||||
import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations
|
|
||||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class TracingMatrixModuleTest {
|
|
||||||
@Test
|
|
||||||
fun `providesTracingFilterConfiguration returns debug config for debug build`() {
|
|
||||||
assertThat(TracingMatrixModule.providesTracingFilterConfiguration(aBuildMeta(BuildType.DEBUG)))
|
|
||||||
.isEqualTo(TracingFilterConfigurations.debug)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `providesTracingFilterConfiguration returns nightly config for nightly build`() {
|
|
||||||
assertThat(TracingMatrixModule.providesTracingFilterConfiguration(aBuildMeta(BuildType.NIGHTLY)))
|
|
||||||
.isEqualTo(TracingFilterConfigurations.nightly)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `providesTracingFilterConfiguration returns release config for release build`() {
|
|
||||||
assertThat(TracingMatrixModule.providesTracingFilterConfiguration(aBuildMeta(BuildType.RELEASE)))
|
|
||||||
.isEqualTo(TracingFilterConfigurations.release)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -37,6 +37,7 @@ fun aRustEventTimelineItem(
|
||||||
origin: EventItemOrigin? = EventItemOrigin.SYNC,
|
origin: EventItemOrigin? = EventItemOrigin.SYNC,
|
||||||
canBeRepliedTo: Boolean = true,
|
canBeRepliedTo: Boolean = true,
|
||||||
shieldsState: ShieldState? = null,
|
shieldsState: ShieldState? = null,
|
||||||
|
localCreatedAt: ULong? = null,
|
||||||
) = EventTimelineItem(
|
) = EventTimelineItem(
|
||||||
isRemote = isRemote,
|
isRemote = isRemote,
|
||||||
eventOrTransactionId = eventOrTransactionId,
|
eventOrTransactionId = eventOrTransactionId,
|
||||||
|
|
@ -51,6 +52,7 @@ fun aRustEventTimelineItem(
|
||||||
reactions = reactions,
|
reactions = reactions,
|
||||||
readReceipts = readReceipts,
|
readReceipts = readReceipts,
|
||||||
origin = origin,
|
origin = origin,
|
||||||
|
localCreatedAt = localCreatedAt,
|
||||||
lazyProvider = FakeRustLazyTimelineItemProvider(
|
lazyProvider = FakeRustLazyTimelineItemProvider(
|
||||||
debugInfo = debugInfo,
|
debugInfo = debugInfo,
|
||||||
shieldsState = shieldsState,
|
shieldsState = shieldsState,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||||
import org.matrix.rustcomponents.sdk.JoinRule
|
import org.matrix.rustcomponents.sdk.JoinRule
|
||||||
import org.matrix.rustcomponents.sdk.Membership
|
import org.matrix.rustcomponents.sdk.Membership
|
||||||
import org.matrix.rustcomponents.sdk.RoomHero
|
import org.matrix.rustcomponents.sdk.RoomHero
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomHistoryVisibility
|
||||||
import org.matrix.rustcomponents.sdk.RoomInfo
|
import org.matrix.rustcomponents.sdk.RoomInfo
|
||||||
import org.matrix.rustcomponents.sdk.RoomMember
|
import org.matrix.rustcomponents.sdk.RoomMember
|
||||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
import org.matrix.rustcomponents.sdk.RoomNotificationMode
|
||||||
|
|
@ -49,6 +50,7 @@ fun aRustRoomInfo(
|
||||||
pinnedEventIds: List<String> = listOf(),
|
pinnedEventIds: List<String> = listOf(),
|
||||||
roomCreator: UserId? = null,
|
roomCreator: UserId? = null,
|
||||||
joinRule: JoinRule? = null,
|
joinRule: JoinRule? = null,
|
||||||
|
historyVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Joined,
|
||||||
) = RoomInfo(
|
) = RoomInfo(
|
||||||
id = id,
|
id = id,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
|
|
@ -81,4 +83,5 @@ fun aRustRoomInfo(
|
||||||
pinnedEventIds = pinnedEventIds,
|
pinnedEventIds = pinnedEventIds,
|
||||||
creator = roomCreator?.value,
|
creator = roomCreator?.value,
|
||||||
joinRule = joinRule,
|
joinRule = joinRule,
|
||||||
|
historyVisibility = historyVisibility
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ fun aRustRoomMember(
|
||||||
powerLevel: Long = 0L,
|
powerLevel: Long = 0L,
|
||||||
isIgnored: Boolean = false,
|
isIgnored: Boolean = false,
|
||||||
role: RoomMemberRole = RoomMemberRole.USER,
|
role: RoomMemberRole = RoomMemberRole.USER,
|
||||||
|
membershipChangeReason: String? = null,
|
||||||
) = RoomMember(
|
) = RoomMember(
|
||||||
userId = userId.value,
|
userId = userId.value,
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
|
|
@ -31,4 +32,5 @@ fun aRustRoomMember(
|
||||||
normalizedPowerLevel = powerLevel,
|
normalizedPowerLevel = powerLevel,
|
||||||
isIgnored = isIgnored,
|
isIgnored = isIgnored,
|
||||||
suggestedRoleForPowerLevel = role,
|
suggestedRoleForPowerLevel = role,
|
||||||
|
membershipChangeReason = membershipChangeReason,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.preferences.api.store
|
package io.element.android.libraries.preferences.api.store
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface AppPreferencesStore {
|
interface AppPreferencesStore {
|
||||||
|
|
@ -25,5 +26,8 @@ interface AppPreferencesStore {
|
||||||
suspend fun setHideImagesAndVideos(value: Boolean)
|
suspend fun setHideImagesAndVideos(value: Boolean)
|
||||||
fun doesHideImagesAndVideosFlow(): Flow<Boolean>
|
fun doesHideImagesAndVideosFlow(): Flow<Boolean>
|
||||||
|
|
||||||
|
suspend fun setTracingLogLevel(logLevel: LogLevel)
|
||||||
|
fun getTracingLogLevelFlow(): Flow<LogLevel>
|
||||||
|
|
||||||
suspend fun reset()
|
suspend fun reset()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import io.element.android.libraries.core.meta.BuildMeta
|
||||||
import io.element.android.libraries.core.meta.BuildType
|
import io.element.android.libraries.core.meta.BuildType
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import io.element.android.libraries.di.ApplicationContext
|
import io.element.android.libraries.di.ApplicationContext
|
||||||
|
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
@ -31,6 +32,7 @@ private val customElementCallBaseUrlKey = stringPreferencesKey("elementCallBaseU
|
||||||
private val themeKey = stringPreferencesKey("theme")
|
private val themeKey = stringPreferencesKey("theme")
|
||||||
private val simplifiedSlidingSyncKey = booleanPreferencesKey("useSimplifiedSlidingSync")
|
private val simplifiedSlidingSyncKey = booleanPreferencesKey("useSimplifiedSlidingSync")
|
||||||
private val hideImagesAndVideosKey = booleanPreferencesKey("hideImagesAndVideos")
|
private val hideImagesAndVideosKey = booleanPreferencesKey("hideImagesAndVideos")
|
||||||
|
private val logLevelKey = stringPreferencesKey("logLevel")
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
class DefaultAppPreferencesStore @Inject constructor(
|
class DefaultAppPreferencesStore @Inject constructor(
|
||||||
|
|
@ -104,7 +106,27 @@ class DefaultAppPreferencesStore @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun setTracingLogLevel(logLevel: LogLevel) {
|
||||||
|
store.edit { prefs ->
|
||||||
|
prefs[logLevelKey] = logLevel.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTracingLogLevelFlow(): Flow<LogLevel> {
|
||||||
|
return store.data.map { prefs ->
|
||||||
|
prefs[logLevelKey]?.let { LogLevel.valueOf(it) } ?: buildMeta.defaultLogLevel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun reset() {
|
override suspend fun reset() {
|
||||||
store.edit { it.clear() }
|
store.edit { it.clear() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun BuildMeta.defaultLogLevel(): LogLevel {
|
||||||
|
return when (buildType) {
|
||||||
|
BuildType.DEBUG -> LogLevel.TRACE
|
||||||
|
BuildType.NIGHTLY -> LogLevel.DEBUG
|
||||||
|
BuildType.RELEASE -> LogLevel.INFO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.preferences.test
|
package io.element.android.libraries.preferences.test
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.tracing.LogLevel
|
||||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -16,13 +17,15 @@ class InMemoryAppPreferencesStore(
|
||||||
hideImagesAndVideos: Boolean = false,
|
hideImagesAndVideos: Boolean = false,
|
||||||
customElementCallBaseUrl: String? = null,
|
customElementCallBaseUrl: String? = null,
|
||||||
theme: String? = null,
|
theme: String? = null,
|
||||||
simplifiedSlidingSyncEnabled: Boolean = false
|
simplifiedSlidingSyncEnabled: Boolean = false,
|
||||||
|
logLevel: LogLevel = LogLevel.INFO,
|
||||||
) : AppPreferencesStore {
|
) : AppPreferencesStore {
|
||||||
private val isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled)
|
private val isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled)
|
||||||
private val hideImagesAndVideos = MutableStateFlow(hideImagesAndVideos)
|
private val hideImagesAndVideos = MutableStateFlow(hideImagesAndVideos)
|
||||||
private val customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl)
|
private val customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl)
|
||||||
private val theme = MutableStateFlow(theme)
|
private val theme = MutableStateFlow(theme)
|
||||||
private val simplifiedSlidingSyncEnabled = MutableStateFlow(simplifiedSlidingSyncEnabled)
|
private val simplifiedSlidingSyncEnabled = MutableStateFlow(simplifiedSlidingSyncEnabled)
|
||||||
|
private val logLevel = MutableStateFlow(logLevel)
|
||||||
|
|
||||||
override suspend fun setDeveloperModeEnabled(enabled: Boolean) {
|
override suspend fun setDeveloperModeEnabled(enabled: Boolean) {
|
||||||
isDeveloperModeEnabled.value = enabled
|
isDeveloperModeEnabled.value = enabled
|
||||||
|
|
@ -64,6 +67,14 @@ class InMemoryAppPreferencesStore(
|
||||||
return hideImagesAndVideos
|
return hideImagesAndVideos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun setTracingLogLevel(logLevel: LogLevel) {
|
||||||
|
this.logLevel.value = logLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTracingLogLevelFlow(): Flow<LogLevel> {
|
||||||
|
return logLevel
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun reset() {
|
override suspend fun reset() {
|
||||||
// No op
|
// No op
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:4f756552f2d88693ae654f92c24b5c23a31cfa6086309e0996444f17bd33f07a
|
|
||||||
size 312449
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:6992b4234fab8b623c05c01c6823791f09bf561b83052cd57d69952380762557
|
|
||||||
size 393746
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:7f1df59fddb1576e385b2fb02cefa904b445376ceb4f1d6ee307e1fcd897d25d
|
|
||||||
size 38164
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:98bd0b6bf5d549ccb7c306dedc807aeb6bd98e77be7b6d8f73ce444000872624
|
|
||||||
size 36727
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:ca9a1d1bb7d4de0b8a5f5efd62cde248f0c9e07b88b2fbdbc598d922003a7f02
|
||||||
|
size 31032
|
||||||
Loading…
Add table
Add a link
Reference in a new issue