Display cache size in the developer settings (#643)
This commit is contained in:
parent
f014f0a3ae
commit
2a7d252a4e
12 changed files with 283 additions and 12 deletions
|
|
@ -26,6 +26,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.execute
|
||||
|
|
@ -41,6 +42,7 @@ import javax.inject.Inject
|
|||
|
||||
class DeveloperSettingsPresenter @Inject constructor(
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val computeCacheSizeUseCase: ComputeCacheSizeUseCase,
|
||||
private val clearCacheUseCase: ClearCacheUseCase,
|
||||
) : Presenter<DeveloperSettingsState> {
|
||||
|
||||
|
|
@ -53,6 +55,9 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
val enabledFeatures = remember {
|
||||
mutableStateMapOf<String, Boolean>()
|
||||
}
|
||||
val cacheSize = remember {
|
||||
mutableStateOf<Async<Long>>(Async.Uninitialized)
|
||||
}
|
||||
val clearCacheAction = remember {
|
||||
mutableStateOf<Async<Unit>>(Async.Uninitialized)
|
||||
}
|
||||
|
|
@ -64,6 +69,10 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
}
|
||||
val featureUiModels = createUiModels(features, enabledFeatures)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
// Compute cache size each time the clear cache action value is changed
|
||||
LaunchedEffect(clearCacheAction.value) {
|
||||
computeCacheSize(cacheSize)
|
||||
}
|
||||
|
||||
fun handleEvents(event: DeveloperSettingsEvents) {
|
||||
when (event) {
|
||||
|
|
@ -79,6 +88,7 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
|
||||
return DeveloperSettingsState(
|
||||
features = featureUiModels.toImmutableList(),
|
||||
cacheSizeInBytes = cacheSize.value,
|
||||
clearCacheAction = clearCacheAction.value,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
|
|
@ -115,6 +125,12 @@ class DeveloperSettingsPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.computeCacheSize(cacheSize: MutableState<Async<Long>>) = launch {
|
||||
suspend {
|
||||
computeCacheSizeUseCase.execute()
|
||||
}.execute(cacheSize)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.clearCache(clearCacheAction: MutableState<Async<Unit>>) = launch {
|
||||
suspend {
|
||||
clearCacheUseCase.execute()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
|
||||
data class DeveloperSettingsState constructor(
|
||||
val features: ImmutableList<FeatureUiModel>,
|
||||
val cacheSizeInBytes: Async<Long>,
|
||||
val clearCacheAction: Async<Unit>,
|
||||
val eventSink: (DeveloperSettingsEvents) -> Unit
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSe
|
|||
|
||||
fun aDeveloperSettingsState() = DeveloperSettingsState(
|
||||
features = aFeatureUiModelList(),
|
||||
cacheSizeInBytes = Async.Success(0L),
|
||||
clearCacheAction = Async.Uninitialized,
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.architecture.isLoading
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||
|
|
@ -55,11 +56,15 @@ fun DeveloperSettingsView(
|
|||
onClick = onOpenShowkase
|
||||
)
|
||||
}
|
||||
val cache = state.cacheSizeInBytes
|
||||
PreferenceCategory(title = "Cache") {
|
||||
PreferenceText(
|
||||
title = "Clear cache",
|
||||
icon = Icons.Default.Delete,
|
||||
loadingCurrentValue = state.clearCacheAction.isLoading(),
|
||||
currentValue = if (cache is Async.Success) {
|
||||
"${cache.state} bytes"
|
||||
} else null,
|
||||
loadingCurrentValue = state.cacheSizeInBytes.isLoading() || state.clearCacheAction.isLoading(),
|
||||
onClick = {
|
||||
if (state.clearCacheAction.isLoading().not()) {
|
||||
state.eventSink(DeveloperSettingsEvents.ClearCache)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.preferences.impl.tasks
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.file.getSizeOfFiles
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
interface ComputeCacheSizeUseCase {
|
||||
suspend fun execute(): Long
|
||||
}
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultComputeCacheSizeUseCase @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
) : ComputeCacheSizeUseCase {
|
||||
override suspend fun execute(): Long = withContext(coroutineDispatchers.io) {
|
||||
var cumulativeSize = 0L
|
||||
cumulativeSize += matrixClient.getCacheSize()
|
||||
cumulativeSize += context.cacheDir.getSizeOfFiles()
|
||||
cumulativeSize
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import app.cash.molecule.moleculeFlow
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
|
|
@ -32,7 +33,8 @@ class DeveloperSettingsPresenterTest {
|
|||
fun `present - ensures initial state is correct`() = runTest {
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
FakeClearCacheUseCase()
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
FakeClearCacheUseCase(),
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -40,6 +42,7 @@ class DeveloperSettingsPresenterTest {
|
|||
val initialState = awaitItem()
|
||||
assertThat(initialState.features).isEmpty()
|
||||
assertThat(initialState.clearCacheAction).isEqualTo(Async.Uninitialized)
|
||||
assertThat(initialState.cacheSizeInBytes).isEqualTo(Async.Uninitialized)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +51,8 @@ class DeveloperSettingsPresenterTest {
|
|||
fun `present - ensures feature list is loaded`() = runTest {
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
FakeClearCacheUseCase()
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
FakeClearCacheUseCase(),
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -64,7 +68,8 @@ class DeveloperSettingsPresenterTest {
|
|||
fun `present - ensures state is updated when enabled feature event is triggered`() = runTest {
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
FakeClearCacheUseCase()
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
FakeClearCacheUseCase(),
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -86,7 +91,8 @@ class DeveloperSettingsPresenterTest {
|
|||
val clearCacheUseCase = FakeClearCacheUseCase()
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService(),
|
||||
clearCacheUseCase
|
||||
FakeComputeCacheSizeUseCase(),
|
||||
clearCacheUseCase,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
|
|
@ -97,8 +103,10 @@ class DeveloperSettingsPresenterTest {
|
|||
initialState.eventSink(DeveloperSettingsEvents.ClearCache)
|
||||
val stateAfterEvent = awaitItem()
|
||||
assertThat(stateAfterEvent.clearCacheAction).isInstanceOf(Async.Loading::class.java)
|
||||
skipItems(1)
|
||||
assertThat(awaitItem().clearCacheAction).isInstanceOf(Async.Success::class.java)
|
||||
assertThat(clearCacheUseCase.executeHasBeenCalled).isTrue()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.preferences.impl.tasks
|
||||
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeComputeCacheSizeUseCase : ComputeCacheSizeUseCase {
|
||||
override suspend fun execute() = simulateLongTask {
|
||||
0L
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue