From b201b40639763cac927289619e45b05fb1118cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 16 Dec 2025 16:53:16 +0100 Subject: [PATCH] Add a new entry in the developer settings to check the store sizes --- .../developer/DeveloperSettingsPresenter.kt | 33 +++++++++++++++++++ .../impl/developer/DeveloperSettingsState.kt | 2 ++ .../DeveloperSettingsStateProvider.kt | 2 ++ .../impl/developer/DeveloperSettingsView.kt | 20 +++++++++++ .../DeveloperSettingsPresenterTest.kt | 5 +++ 5 files changed, 62 insertions(+) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index db35a1dc83..a0d96be540 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -31,21 +31,26 @@ 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.VacuumStoresUseCase import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState +import io.element.android.libraries.androidutils.filesize.FileSizeFormatter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState +import io.element.android.libraries.core.data.ByteUnit import io.element.android.libraries.core.extensions.runCatchingExceptions 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.FeatureFlags import io.element.android.libraries.featureflag.ui.model.FeatureUiModel +import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.preferences.api.store.AppPreferencesStore import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map @@ -63,6 +68,8 @@ class DeveloperSettingsPresenter( private val buildMeta: BuildMeta, private val enterpriseService: EnterpriseService, private val vacuumStoresUseCase: VacuumStoresUseCase, + private val databaseSizesUseCase: GetDatabaseSizesUseCase, + private val fileSizeFormatter: FileSizeFormatter, ) : Presenter { @Composable override fun present(): DeveloperSettingsState { @@ -73,6 +80,9 @@ class DeveloperSettingsPresenter( val cacheSize = remember { mutableStateOf>(AsyncData.Uninitialized) } + val databaseSizes = remember { + mutableStateOf>>(AsyncData.Uninitialized) + } val clearCacheAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } @@ -96,6 +106,7 @@ class DeveloperSettingsPresenter( } LaunchedEffect(Unit) { + computeDatabaseSizes(databaseSizes) featureFlagService.getAvailableFeatures() .run { // Never display room directory search in release builds for Play Store @@ -162,6 +173,7 @@ class DeveloperSettingsPresenter( return DeveloperSettingsState( features = featureUiModels, cacheSize = cacheSize.value, + databaseSizes = databaseSizes.value, clearCacheAction = clearCacheAction.value, rageshakeState = rageshakeState, customElementCallBaseUrlState = CustomElementCallBaseUrlState( @@ -214,6 +226,27 @@ class DeveloperSettingsPresenter( }.runCatchingUpdatingState(cacheSize) } + private fun CoroutineScope.computeDatabaseSizes(databaseSizes: MutableState>>) = launch { + suspend { + databaseSizesUseCase(sessionId).getOrThrow().let { sizes -> + buildMap { + sizes.stateStore?.let { stateStoreSize -> + put("State store", fileSizeFormatter.format(stateStoreSize.into(ByteUnit.BYTES), useShortFormat = true)) + } + sizes.eventCacheStore?.let { eventCacheStoreSize -> + put("Event cache store", fileSizeFormatter.format(eventCacheStoreSize.into(ByteUnit.BYTES), useShortFormat = true)) + } + sizes.mediaStore?.let { mediaStoreSize -> + put("Media store", fileSizeFormatter.format(mediaStoreSize.into(ByteUnit.BYTES), useShortFormat = true)) + } + sizes.cryptoStore?.let { cryptoStoreSize -> + put("Crypto store", fileSizeFormatter.format(cryptoStoreSize.into(ByteUnit.BYTES), useShortFormat = true)) + } + } + }.toImmutableMap() + }.runCatchingUpdatingState(databaseSizes) + } + private fun CoroutineScope.clearCache(clearCacheAction: MutableState>) = launch { suspend { clearCacheUseCase() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt index f97270dc7a..920c8ec95c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt @@ -15,10 +15,12 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.featureflag.ui.model.FeatureUiModel import io.element.android.libraries.matrix.api.tracing.TraceLogPack import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap data class DeveloperSettingsState( val features: ImmutableList, val cacheSize: AsyncData, + val databaseSizes: AsyncData>, val rageshakeState: RageshakePreferencesState, val clearCacheAction: AsyncAction, val customElementCallBaseUrlState: CustomElementCallBaseUrlState, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt index ea16ed9f0f..9ac4fdfcc8 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt @@ -15,6 +15,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.featureflag.ui.model.aFeatureUiModelList import io.element.android.libraries.matrix.api.tracing.TraceLogPack +import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableList open class DeveloperSettingsStateProvider : PreviewParameterProvider { @@ -47,6 +48,7 @@ fun aDeveloperSettingsState( features = aFeatureUiModelList(), rageshakeState = aRageshakePreferencesState(), cacheSize = AsyncData.Success("1.2 MB"), + databaseSizes = AsyncData.Success(persistentMapOf("state_store" to "1.2MB")), clearCacheAction = clearCacheAction, customElementCallBaseUrlState = customElementCallBaseUrlState, tracingLogLevel = AsyncData.Success(LogLevelItem.INFO), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 2bfb2f086a..444a391d43 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -9,6 +9,7 @@ package io.element.android.features.preferences.impl.developer import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.progressSemantics @@ -146,6 +147,25 @@ fun DeveloperSettingsView( } val cache = state.cacheSize PreferenceCategory(title = "Cache") { + ListItem( + headlineContent = { Text("Database sizes") }, + supportingContent = { + if (state.databaseSizes.isLoading()) { + Text("Computing...") + } else { + val dbSizes = state.databaseSizes.dataOrNull() + if (dbSizes != null && dbSizes.isNotEmpty()) { + Column { + for ((dbName, size) in dbSizes) { + Text("$dbName: $size") + } + } + } else { + Text("Unknown") + } + } + } + ) ListItem( headlineContent = { Text("Vacuum stores") diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index 75ae12b714..fa5f5fd0f6 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt @@ -19,6 +19,7 @@ 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.VacuumStoresUseCase import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState +import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.meta.BuildMeta @@ -27,6 +28,7 @@ import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeature import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.core.aBuildMeta @@ -249,6 +251,7 @@ class DeveloperSettingsPresenterTest { buildMeta: BuildMeta = aBuildMeta(), enterpriseService: EnterpriseService = FakeEnterpriseService(), vacuumStoresUseCase: VacuumStoresUseCase = VacuumStoresUseCase {}, + datbaseSizesUseCase: GetDatabaseSizesUseCase = GetDatabaseSizesUseCase { Result.success(emptyMap()) }, ): DeveloperSettingsPresenter { return DeveloperSettingsPresenter( sessionId = sessionId, @@ -260,6 +263,8 @@ class DeveloperSettingsPresenterTest { buildMeta = buildMeta, enterpriseService = enterpriseService, vacuumStoresUseCase = vacuumStoresUseCase, + databaseSizesUseCase = datbaseSizesUseCase, + fileSizeFormatter = FakeFileSizeFormatter(), ) } }