From 58860a9440f61da5d7504ebe72b1174ae7b9d353 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 21 Jun 2023 09:34:30 +0200 Subject: [PATCH] Close the client before deleting data, and ensure the app is restarted, using a cache Index. --- .../io/element/android/appnav/RootFlowNode.kt | 21 +++++++++++++------ .../impl/tasks/ClearCacheUseCase.kt | 5 +++-- .../libraries/matrix/api/MatrixClient.kt | 4 ++++ .../api/auth/MatrixAuthenticationService.kt | 7 +++++++ .../libraries/matrix/impl/RustMatrixClient.kt | 6 ++++-- .../auth/RustMatrixAuthenticationService.kt | 10 +++++++++ 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt index 78c39f93e6..d8847b55b3 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt @@ -54,7 +54,9 @@ import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.parcelize.Parcelize @@ -88,11 +90,17 @@ class RootFlowNode @AssistedInject constructor( private fun observeLoggedInState() { authenticationService.isLoggedIn() .distinctUntilChanged() + .combine( + authenticationService.cacheIdx().onEach { + Timber.v("cacheIdx=$it") + matrixClientsHolder.removeAll() + } + ) { isLoggedIn, cacheIdx -> isLoggedIn to cacheIdx } .onEach { isLoggedIn -> Timber.v("isLoggedIn=$isLoggedIn") - if (isLoggedIn) { + if (isLoggedIn.first) { tryToRestoreLatestSession( - onSuccess = { switchToLoggedInFlow(it) }, + onSuccess = { switchToLoggedInFlow(it, isLoggedIn.second) }, onFailure = { switchToNotLoggedInFlow() } ) } else { @@ -102,8 +110,8 @@ class RootFlowNode @AssistedInject constructor( .launchIn(lifecycleScope) } - private fun switchToLoggedInFlow(sessionId: SessionId) { - backstack.safeRoot(NavTarget.LoggedInFlow(sessionId)) + private fun switchToLoggedInFlow(sessionId: SessionId, cacheIndex: Int) { + backstack.safeRoot(NavTarget.LoggedInFlow(sessionId, cacheIndex)) } private fun switchToNotLoggedInFlow() { @@ -163,7 +171,7 @@ class RootFlowNode @AssistedInject constructor( object NotLoggedInFlow : NavTarget @Parcelize - data class LoggedInFlow(val sessionId: SessionId) : NavTarget + data class LoggedInFlow(val sessionId: SessionId, val cacheIndex: Int) : NavTarget @Parcelize object BugReport : NavTarget @@ -235,8 +243,9 @@ class RootFlowNode @AssistedInject constructor( } private suspend fun attachSession(sessionId: SessionId): LoggedInFlowNode { + val cacheIdx = authenticationService.cacheIdx().first() return attachChild { - backstack.newRoot(NavTarget.LoggedInFlow(sessionId)) + backstack.newRoot(NavTarget.LoggedInFlow(sessionId, cacheIdx)) } } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt index 4ccfbb6b81..1624a81d64 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt @@ -22,6 +22,7 @@ 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 io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import kotlinx.coroutines.withContext import javax.inject.Inject @@ -34,11 +35,11 @@ class DefaultClearCacheUseCase @Inject constructor( @ApplicationContext private val context: Context, private val matrixClient: MatrixClient, private val coroutineDispatchers: CoroutineDispatchers, + private val authenticationService: MatrixAuthenticationService, ) : ClearCacheUseCase { override suspend fun execute() = withContext(coroutineDispatchers.io) { - matrixClient.stopSync() matrixClient.clearCache() context.cacheDir.deleteRecursively() - matrixClient.startSync() + authenticationService.incrementCacheIdx() } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 479e3169d9..0bd7a8ab22 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -51,6 +51,10 @@ interface MatrixClient : Closeable { fun pushersService(): PushersService fun notificationService(): NotificationService suspend fun getCacheSize(): Long + + /** + * Will close the client and delete the cache data. + */ suspend fun clearCache() suspend fun logout() suspend fun loadUserDisplayName(): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt index c15153876c..8d63642eae 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt @@ -29,6 +29,13 @@ interface MatrixAuthenticationService { suspend fun setHomeserver(homeserver: String): Result suspend fun login(username: String, password: String): Result + /* + * Cache index + */ + + fun cacheIdx(): Flow + fun incrementCacheIdx() + /* * OIDC part. */ diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 03d18abba5..773dc8d03b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -339,11 +339,13 @@ class RustMatrixClient constructor( } override suspend fun getCacheSize(): Long { - return baseDirectory.getCacheSize(userID = client.userId()) + // Do not use client.userId since it can throw if client has been closed (during clear cache) + return baseDirectory.getCacheSize(userID = sessionId.value) } override suspend fun clearCache() { - baseDirectory.deleteSessionDirectory(userID = client.userId()) + close() + baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = false) } override suspend fun logout() = withContext(dispatchers.io) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index d599029923..2e3c406348 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -58,6 +58,8 @@ class RustMatrixAuthenticationService @Inject constructor( private val clock: SystemClock, ) : MatrixAuthenticationService { + private val cacheIdxState = MutableStateFlow(0) + private val authService: RustAuthenticationService = RustAuthenticationService( basePath = baseDirectory.absolutePath, passphrase = null, @@ -71,6 +73,14 @@ class RustMatrixAuthenticationService @Inject constructor( return sessionStore.isLoggedIn() } + override fun incrementCacheIdx() { + cacheIdxState.value++ + } + + override fun cacheIdx(): Flow { + return cacheIdxState + } + override suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io) { sessionStore.getLatestSession()?.userId?.let { SessionId(it) } }