From b9f4403408d1fcebff811eda4b6c8d1f7dcccf6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 19 Jul 2024 14:46:42 +0200 Subject: [PATCH 1/7] Add simplified sliding sync toggle to developer options --- .../impl/developer/DeveloperSettingsEvents.kt | 1 + .../developer/DeveloperSettingsPresenter.kt | 7 ++++++ .../impl/developer/DeveloperSettingsState.kt | 1 + .../DeveloperSettingsStateProvider.kt | 2 ++ .../impl/developer/DeveloperSettingsView.kt | 9 ++++++++ .../DeveloperSettingsPresenterTest.kt | 22 +++++++++++++++++++ .../developer/DeveloperSettingsViewTest.kt | 13 +++++++++++ libraries/matrix/impl/build.gradle.kts | 5 +++-- .../libraries/matrix/impl/RustMatrixClient.kt | 3 +++ .../matrix/impl/RustMatrixClientFactory.kt | 12 +++++++++- .../api/store/AppPreferencesStore.kt | 3 +++ .../impl/store/DefaultAppPreferencesStore.kt | 13 +++++++++++ .../test/InMemoryAppPreferencesStore.kt | 10 +++++++++ 13 files changed, 98 insertions(+), 3 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt index 58f7ac7c02..bb0252e04d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt @@ -21,5 +21,6 @@ import io.element.android.libraries.featureflag.ui.model.FeatureUiModel sealed interface DeveloperSettingsEvents { data class UpdateEnabledFeature(val feature: FeatureUiModel, val isEnabled: Boolean) : DeveloperSettingsEvents data class SetCustomElementCallBaseUrl(val baseUrl: String?) : DeveloperSettingsEvents + data class SetSimplifiedSlidingSyncEnabled(val isEnabled: Boolean) : DeveloperSettingsEvents data object ClearCache : DeveloperSettingsEvents } 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 8295b815c9..4f4b833367 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 @@ -75,6 +75,9 @@ class DeveloperSettingsPresenter @Inject constructor( val customElementCallBaseUrl by appPreferencesStore .getCustomElementCallBaseUrlFlow() .collectAsState(initial = null) + val isSimplifiedSlidingSyncEnabled by appPreferencesStore + .isSimplifiedSlidingSyncEnabledFlow() + .collectAsState(initial = false) LaunchedEffect(Unit) { FeatureFlags.entries @@ -114,6 +117,9 @@ class DeveloperSettingsPresenter @Inject constructor( appPreferencesStore.setCustomElementCallBaseUrl(urlToSave) } DeveloperSettingsEvents.ClearCache -> coroutineScope.clearCache(clearCacheAction) + is DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled -> coroutineScope.launch { + appPreferencesStore.setSimplifiedSlidingSyncEnabled(event.isEnabled) + } } } @@ -127,6 +133,7 @@ class DeveloperSettingsPresenter @Inject constructor( defaultUrl = ElementCallConfig.DEFAULT_BASE_URL, validator = ::customElementCallUrlValidator, ), + isSimpleSlidingSyncEnabled = isSimplifiedSlidingSyncEnabled, eventSink = ::handleEvents ) } 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 9a12823686..91cf7051c3 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 @@ -27,6 +27,7 @@ data class DeveloperSettingsState( val rageshakeState: RageshakePreferencesState, val clearCacheAction: AsyncData, val customElementCallBaseUrlState: CustomElementCallBaseUrlState, + val isSimpleSlidingSyncEnabled: Boolean, val eventSink: (DeveloperSettingsEvents) -> Unit ) 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 fb93a63ffc..01c2971777 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 @@ -39,6 +39,7 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider = AsyncData.Uninitialized, customElementCallBaseUrlState: CustomElementCallBaseUrlState = aCustomElementCallBaseUrlState(), + isSimplifiedSlidingSyncEnabled: Boolean = false, eventSink: (DeveloperSettingsEvents) -> Unit = {}, ) = DeveloperSettingsState( features = aFeatureUiModelList(), @@ -46,6 +47,7 @@ fun aDeveloperSettingsState( cacheSize = AsyncData.Success("1.2 MB"), clearCacheAction = clearCacheAction, customElementCallBaseUrlState = customElementCallBaseUrlState, + isSimpleSlidingSyncEnabled = isSimplifiedSlidingSyncEnabled, eventSink = eventSink, ) 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 dd684fcaaa..3fab690191 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 @@ -26,6 +26,7 @@ import io.element.android.features.preferences.impl.R 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.PreferencePage +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.PreferenceTextField import io.element.android.libraries.designsystem.preview.ElementPreview @@ -60,6 +61,14 @@ fun DeveloperSettingsView( title = "Configure tracing", onClick = onOpenConfigureTracing, ) + PreferenceSwitch( + title = "Enable Simplified Sliding Sync", + subtitle = "This option requires an app restart to work.", + isChecked = state.isSimpleSlidingSyncEnabled, + onCheckedChange = { + state.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(it)) + } + ) } PreferenceCategory(title = "Showkase") { PreferenceText( 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 113125d950..4c7a16f8cd 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 @@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem +import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -55,6 +56,7 @@ class DeveloperSettingsPresenterTest { assertThat(initialState.cacheSize).isEqualTo(AsyncData.Uninitialized) assertThat(initialState.customElementCallBaseUrlState).isNotNull() assertThat(initialState.customElementCallBaseUrlState.baseUrl).isNull() + assertThat(initialState.isSimpleSlidingSyncEnabled).isFalse() val loadedState = awaitItem() assertThat(loadedState.rageshakeState.isEnabled).isFalse() assertThat(loadedState.rageshakeState.isSupported).isTrue() @@ -160,6 +162,26 @@ class DeveloperSettingsPresenterTest { } } + @Test + fun `present - toggling simplified sliding sync changes the preferences`() = runTest { + val preferences = InMemoryAppPreferencesStore() + val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitLastSequentialItem() + assertThat(initialState.isSimpleSlidingSyncEnabled).isFalse() + + initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(true)) + assertThat(awaitItem().isSimpleSlidingSyncEnabled).isTrue() + assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isTrue() + + initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(false)) + assertThat(awaitItem().isSimpleSlidingSyncEnabled).isFalse() + assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isFalse() + } + } + private fun createDeveloperSettingsPresenter( featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), cacheSizeUseCase: FakeComputeCacheSizeUseCase = FakeComputeCacheSizeUseCase(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt index 7288b76d3c..aa993c39bc 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt @@ -109,6 +109,19 @@ class DeveloperSettingsViewTest { rule.onNodeWithText("Clear cache").performClick() eventsRecorder.assertSingle(DeveloperSettingsEvents.ClearCache) } + + @Config(qualifiers = "h1500dp") + @Test + fun `clicking on the simplified sliding sync switch emits the expected event`() { + val eventsRecorder = EventsRecorder() + rule.setDeveloperSettingsView( + state = aDeveloperSettingsState( + eventSink = eventsRecorder + ), + ) + rule.onNodeWithText("Enable Simplified Sliding Sync").performClick() + eventsRecorder.assertSingle(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(true)) + } } private fun AndroidComposeTestRule.setDeveloperSettingsView( diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 523ea2fda1..c74cb6ce07 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -37,12 +37,13 @@ dependencies { debugImplementation(libs.matrix.sdk) } implementation(projects.appconfig) - implementation(projects.libraries.di) implementation(projects.libraries.androidutils) + implementation(projects.libraries.di) + implementation(projects.libraries.featureflag.api) implementation(projects.libraries.network) + implementation(projects.libraries.preferences.api) implementation(projects.services.analytics.api) implementation(projects.services.toolbox.api) - implementation(projects.libraries.featureflag.api) api(projects.libraries.matrix.api) implementation(libs.dagger) implementation(projects.libraries.core) 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 db86e1a2a5..1f9a531027 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 @@ -69,6 +69,7 @@ import io.element.android.libraries.matrix.impl.util.anonymizedTokens import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService +import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.collections.immutable.ImmutableList @@ -126,6 +127,7 @@ class RustMatrixClient( private val baseDirectory: File, baseCacheDirectory: File, private val clock: SystemClock, + private val appPreferencesStore: AppPreferencesStore, ) : MatrixClient { override val sessionId: UserId = UserId(client.userId()) override val deviceId: String = client.deviceId() @@ -554,6 +556,7 @@ class RustMatrixClient( } close() deleteSessionDirectory(deleteCryptoDb = true) + appPreferencesStore.setSimplifiedSlidingSyncEnabled(false) if (removeSession) { sessionStore.removeSession(sessionId.value) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index 7ddf54a300..f9c2a5b6d1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -23,10 +23,12 @@ import io.element.android.libraries.matrix.impl.certificates.UserCertificatesPro import io.element.android.libraries.matrix.impl.proxy.ProxyProvider import io.element.android.libraries.matrix.impl.util.anonymizedTokens import io.element.android.libraries.network.useragent.UserAgentProvider +import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.Session @@ -46,9 +48,14 @@ class RustMatrixClientFactory @Inject constructor( private val proxyProvider: ProxyProvider, private val clock: SystemClock, private val utdTracker: UtdTracker, + private val appPreferencesStore: AppPreferencesStore, ) { suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { - val client = getBaseClientBuilder(sessionData.sessionPath, sessionData.passphrase) + val client = getBaseClientBuilder( + sessionPath = sessionData.sessionPath, + passphrase = sessionData.passphrase, + useSimplifiedSlidingSync = appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first(), + ) .homeserverUrl(sessionData.homeserverUrl) .username(sessionData.userId) .use { it.build() } @@ -70,6 +77,7 @@ class RustMatrixClientFactory @Inject constructor( baseDirectory = baseDirectory, baseCacheDirectory = cacheDirectory, clock = clock, + appPreferencesStore = appPreferencesStore, ).also { Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") } @@ -79,6 +87,7 @@ class RustMatrixClientFactory @Inject constructor( sessionPath: String, passphrase: String?, slidingSyncProxy: String? = null, + useSimplifiedSlidingSync: Boolean = false, ): ClientBuilder { return ClientBuilder() .sessionPath(sessionPath) @@ -88,6 +97,7 @@ class RustMatrixClientFactory @Inject constructor( .addRootCertificates(userCertificatesProvider.provides()) .autoEnableBackups(true) .autoEnableCrossSigning(true) + .simplifiedSlidingSync(useSimplifiedSlidingSync) .run { // Workaround for non-nullable proxy parameter in the SDK, since each call to the ClientBuilder returns a new reference we need to keep proxyProvider.provides()?.let { proxy(it) } ?: this diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt index 5d12adbe74..73a42a4ff4 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt @@ -28,5 +28,8 @@ interface AppPreferencesStore { suspend fun setTheme(theme: String) fun getThemeFlow(): Flow + suspend fun setSimplifiedSlidingSyncEnabled(enabled: Boolean) + fun isSimplifiedSlidingSyncEnabledFlow(): Flow + suspend fun reset() } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt index d25cda96d0..e80c6d60ad 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt @@ -38,6 +38,7 @@ private val Context.dataStore: DataStore by preferencesDataStore(na private val developerModeKey = booleanPreferencesKey("developerMode") private val customElementCallBaseUrlKey = stringPreferencesKey("elementCallBaseUrl") private val themeKey = stringPreferencesKey("theme") +private val simplifiedSlidingSyncKey = booleanPreferencesKey("useSimplifiedSlidingSync") @ContributesBinding(AppScope::class) class DefaultAppPreferencesStore @Inject constructor( @@ -87,6 +88,18 @@ class DefaultAppPreferencesStore @Inject constructor( } } + override suspend fun setSimplifiedSlidingSyncEnabled(enabled: Boolean) { + store.edit { prefs -> + prefs[simplifiedSlidingSyncKey] = enabled + } + } + + override fun isSimplifiedSlidingSyncEnabledFlow(): Flow { + return store.data.map { prefs -> + prefs[simplifiedSlidingSyncKey] ?: false + } + } + override suspend fun reset() { store.edit { it.clear() } } diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt index c920f8c6b3..432c7a9772 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt @@ -24,10 +24,12 @@ class InMemoryAppPreferencesStore( isDeveloperModeEnabled: Boolean = false, customElementCallBaseUrl: String? = null, theme: String? = null, + simplifiedSlidingSyncEnabled: Boolean = false ) : AppPreferencesStore { private val isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled) private val customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl) private val theme = MutableStateFlow(theme) + private val simplifiedSlidingSyncEnabled = MutableStateFlow(simplifiedSlidingSyncEnabled) override suspend fun setDeveloperModeEnabled(enabled: Boolean) { isDeveloperModeEnabled.value = enabled @@ -53,6 +55,14 @@ class InMemoryAppPreferencesStore( return theme } + override suspend fun setSimplifiedSlidingSyncEnabled(enabled: Boolean) { + simplifiedSlidingSyncEnabled.value = enabled + } + + override fun isSimplifiedSlidingSyncEnabledFlow(): Flow { + return simplifiedSlidingSyncEnabled + } + override suspend fun reset() { // No op } From fa0430927a431dfd4d0b82f863de5337b95daf11 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 19 Jul 2024 12:58:11 +0000 Subject: [PATCH 2/7] Update screenshots --- ...ferences.impl.developer_DeveloperSettingsView_Day_0_en.png | 4 ++-- ...ferences.impl.developer_DeveloperSettingsView_Day_1_en.png | 4 ++-- ...ferences.impl.developer_DeveloperSettingsView_Day_2_en.png | 4 ++-- ...rences.impl.developer_DeveloperSettingsView_Night_0_en.png | 4 ++-- ...rences.impl.developer_DeveloperSettingsView_Night_1_en.png | 4 ++-- ...rences.impl.developer_DeveloperSettingsView_Night_2_en.png | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png index e3a0f6e67e..1aca7ec0cf 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db1631ff53075b31c674cde4bf25cc2876cda8053ffcb593bf6c497c5c41d0c4 -size 49825 +oid sha256:2254c087a6dcf26a725ac3bea499d448c1e652a5f558f400500b6ac47290cea0 +size 55170 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png index e3a0f6e67e..1aca7ec0cf 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db1631ff53075b31c674cde4bf25cc2876cda8053ffcb593bf6c497c5c41d0c4 -size 49825 +oid sha256:2254c087a6dcf26a725ac3bea499d448c1e652a5f558f400500b6ac47290cea0 +size 55170 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png index df320f9fd7..ab696e6c2f 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7f5ea92f8413f6b6dc92d074038727036912ef1828210d0c4d6e36fb0f7e5d3 -size 48352 +oid sha256:e3db370fb5b5be9e6585367634f2d80292b21145a275b4bcbb3f4aea54cb259a +size 53724 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png index 91914c2851..e28bcd2b6d 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ead9e1fe98bc9ffd33d3768e6983964768edbf3bb4c75cd78a82da5ec0eae03b -size 48275 +oid sha256:c38acd466d6d6bc4b0b03d7588fed15532338a37f678baac5cf1902bd5eab5b8 +size 53240 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png index 91914c2851..e28bcd2b6d 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ead9e1fe98bc9ffd33d3768e6983964768edbf3bb4c75cd78a82da5ec0eae03b -size 48275 +oid sha256:c38acd466d6d6bc4b0b03d7588fed15532338a37f678baac5cf1902bd5eab5b8 +size 53240 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png index c3c8c2ac12..df1ab02517 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee30083ec3b87ba84a34bdee7b10d50939f31c596c3358bdca9b914ba001d198 -size 46843 +oid sha256:42582ba2979c38cc0d2f8c3986b22c0dc31f8bc952e39f67669e46f5e42c6239 +size 51792 From 114b12cbc7733f284ffc191ab4e9dc5f635a0dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 19 Jul 2024 15:17:16 +0200 Subject: [PATCH 3/7] Fix build issues --- samples/minimal/build.gradle.kts | 1 + .../kotlin/io/element/android/samples/minimal/MainActivity.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/samples/minimal/build.gradle.kts b/samples/minimal/build.gradle.kts index 91cc4bd110..f754b28a4e 100644 --- a/samples/minimal/build.gradle.kts +++ b/samples/minimal/build.gradle.kts @@ -60,6 +60,7 @@ dependencies { implementation(projects.libraries.eventformatter.impl) implementation(projects.libraries.fullscreenintent.impl) implementation(projects.libraries.preferences.impl) + implementation(projects.libraries.preferences.test) implementation(projects.libraries.indicator.impl) implementation(projects.features.invite.impl) implementation(projects.features.roomlist.impl) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index d13cc429dc..cab4c09660 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.impl.analytics.UtdTracker import io.element.android.libraries.matrix.impl.auth.OidcConfigurationProvider import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService import io.element.android.libraries.network.useragent.SimpleUserAgentProvider +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore import io.element.android.services.analytics.noop.NoopAnalyticsService @@ -62,6 +63,7 @@ class MainActivity : ComponentActivity() { proxyProvider = proxyProvider, clock = DefaultSystemClock(), utdTracker = UtdTracker(NoopAnalyticsService()), + appPreferencesStore = InMemoryAppPreferencesStore(), ), passphraseGenerator = NullPassphraseGenerator(), oidcConfigurationProvider = OidcConfigurationProvider(baseDirectory), From ff6ea4323713ce56dc5cfa6d7b170e56acd748df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 19 Jul 2024 16:32:25 +0200 Subject: [PATCH 4/7] Don't reset the SSS toggle when logged out. Instead, force a logout when toggling the option so user needs to log in again and create a new session using SSS. --- .../features/logout/api/LogoutUseCase.kt | 28 ++++++++++++++++ .../logout/impl/DefaultLogoutUseCase.kt | 32 +++++++++++++++++++ .../developer/DeveloperSettingsPresenter.kt | 3 ++ .../impl/developer/DeveloperSettingsView.kt | 2 +- .../DeveloperSettingsPresenterTest.kt | 19 +++++++++-- .../libraries/matrix/impl/RustMatrixClient.kt | 3 -- .../matrix/impl/RustMatrixClientFactory.kt | 1 - 7 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt create mode 100644 features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt new file mode 100644 index 0000000000..397f5ebed7 --- /dev/null +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 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 + * + * https://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.logout.api + +/** + * Used to trigger a log out of the current user from any part of the app. + */ +interface LogoutUseCase { + /** + * Log out the current user and then perform any needed cleanup tasks. + * @param ignoreSdkError if true, the SDK error will be ignored and the user will be logged out anyway. + */ + suspend fun logout(ignoreSdkError: Boolean) +} diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt new file mode 100644 index 0000000000..52bf0425d4 --- /dev/null +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 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 + * + * https://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.logout.impl + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.MatrixClient +import javax.inject.Inject + +@ContributesBinding(SessionScope::class) +class DefaultLogoutUseCase @Inject constructor( + private val matrixClient: MatrixClient +) : LogoutUseCase { + override suspend fun logout(ignoreSdkError: Boolean) { + matrixClient.logout(ignoreSdkError = ignoreSdkError) + } +} 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 4f4b833367..7b95463ac8 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 @@ -28,6 +28,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.snapshots.SnapshotStateMap import io.element.android.appconfig.ElementCallConfig +import io.element.android.features.logout.api.LogoutUseCase import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter @@ -55,6 +56,7 @@ class DeveloperSettingsPresenter @Inject constructor( private val rageshakePresenter: RageshakePreferencesPresenter, private val appPreferencesStore: AppPreferencesStore, private val buildMeta: BuildMeta, + private val logoutUseCase: LogoutUseCase, ) : Presenter { @Composable override fun present(): DeveloperSettingsState { @@ -119,6 +121,7 @@ class DeveloperSettingsPresenter @Inject constructor( DeveloperSettingsEvents.ClearCache -> coroutineScope.clearCache(clearCacheAction) is DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled -> coroutineScope.launch { appPreferencesStore.setSimplifiedSlidingSyncEnabled(event.isEnabled) + logoutUseCase.logout(ignoreSdkError = true) } } } 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 3fab690191..f46637e6cb 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 @@ -63,7 +63,7 @@ fun DeveloperSettingsView( ) PreferenceSwitch( title = "Enable Simplified Sliding Sync", - subtitle = "This option requires an app restart to work.", + subtitle = "When toggled you'll be logged out of the app and will need to log in again.", isChecked = state.isSimpleSlidingSyncEnabled, onCheckedChange = { state.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(it)) 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 4c7a16f8cd..be3c6bbb20 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 @@ -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.appconfig.ElementCallConfig +import io.element.android.features.logout.api.LogoutUseCase import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter @@ -163,9 +164,10 @@ class DeveloperSettingsPresenterTest { } @Test - fun `present - toggling simplified sliding sync changes the preferences`() = runTest { + fun `present - toggling simplified sliding sync changes the preferences and logs out the user`() = runTest { + val logoutUseCase = FakeLogoutUseCase() val preferences = InMemoryAppPreferencesStore() - val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences) + val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences, logoutUseCase = logoutUseCase) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -175,10 +177,12 @@ class DeveloperSettingsPresenterTest { initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(true)) assertThat(awaitItem().isSimpleSlidingSyncEnabled).isTrue() assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isTrue() + assertThat(logoutUseCase.logoutCallCount).isEqualTo(1) initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(false)) assertThat(awaitItem().isSimpleSlidingSyncEnabled).isFalse() assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isFalse() + assertThat(logoutUseCase.logoutCallCount).isEqualTo(2) } } @@ -189,6 +193,7 @@ class DeveloperSettingsPresenterTest { rageshakePresenter: DefaultRageshakePreferencesPresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore()), preferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(), buildMeta: BuildMeta = aBuildMeta(), + logoutUseCase: FakeLogoutUseCase = FakeLogoutUseCase() ): DeveloperSettingsPresenter { return DeveloperSettingsPresenter( featureFlagService = featureFlagService, @@ -197,6 +202,16 @@ class DeveloperSettingsPresenterTest { rageshakePresenter = rageshakePresenter, appPreferencesStore = preferencesStore, buildMeta = buildMeta, + logoutUseCase = logoutUseCase, ) } } + +private class FakeLogoutUseCase : LogoutUseCase { + var logoutCallCount = 0 + private set + + override suspend fun logout(ignoreSdkError: Boolean) { + logoutCallCount++ + } +} 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 1f9a531027..db86e1a2a5 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 @@ -69,7 +69,6 @@ import io.element.android.libraries.matrix.impl.util.anonymizedTokens import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService -import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.collections.immutable.ImmutableList @@ -127,7 +126,6 @@ class RustMatrixClient( private val baseDirectory: File, baseCacheDirectory: File, private val clock: SystemClock, - private val appPreferencesStore: AppPreferencesStore, ) : MatrixClient { override val sessionId: UserId = UserId(client.userId()) override val deviceId: String = client.deviceId() @@ -556,7 +554,6 @@ class RustMatrixClient( } close() deleteSessionDirectory(deleteCryptoDb = true) - appPreferencesStore.setSimplifiedSlidingSyncEnabled(false) if (removeSession) { sessionStore.removeSession(sessionId.value) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index f9c2a5b6d1..96d8e4939d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -77,7 +77,6 @@ class RustMatrixClientFactory @Inject constructor( baseDirectory = baseDirectory, baseCacheDirectory = cacheDirectory, clock = clock, - appPreferencesStore = appPreferencesStore, ).also { Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") } From a71d7eb2b4fc0146e980393922879676bb5acf4b Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 19 Jul 2024 14:43:41 +0000 Subject: [PATCH 5/7] Update screenshots --- ...ferences.impl.developer_DeveloperSettingsView_Day_0_en.png | 4 ++-- ...ferences.impl.developer_DeveloperSettingsView_Day_1_en.png | 4 ++-- ...ferences.impl.developer_DeveloperSettingsView_Day_2_en.png | 4 ++-- ...rences.impl.developer_DeveloperSettingsView_Night_0_en.png | 4 ++-- ...rences.impl.developer_DeveloperSettingsView_Night_1_en.png | 4 ++-- ...rences.impl.developer_DeveloperSettingsView_Night_2_en.png | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png index 1aca7ec0cf..d46d57762d 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2254c087a6dcf26a725ac3bea499d448c1e652a5f558f400500b6ac47290cea0 -size 55170 +oid sha256:fb9971d6aa7f0734f9a30f83b25a03519f97789618219f0c79efa87d4430ca0f +size 58918 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png index 1aca7ec0cf..d46d57762d 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2254c087a6dcf26a725ac3bea499d448c1e652a5f558f400500b6ac47290cea0 -size 55170 +oid sha256:fb9971d6aa7f0734f9a30f83b25a03519f97789618219f0c79efa87d4430ca0f +size 58918 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png index ab696e6c2f..5119090213 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3db370fb5b5be9e6585367634f2d80292b21145a275b4bcbb3f4aea54cb259a -size 53724 +oid sha256:f44fc0dc3cd92839a7b176b086be75b082a238b9c8cd197f7273b33e7f01c591 +size 57495 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png index e28bcd2b6d..2294fd4c8a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c38acd466d6d6bc4b0b03d7588fed15532338a37f678baac5cf1902bd5eab5b8 -size 53240 +oid sha256:93a6d6723bc89b9660a440848461c1568004d312599d7d2d2b94299d6aa47c0a +size 57037 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png index e28bcd2b6d..2294fd4c8a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c38acd466d6d6bc4b0b03d7588fed15532338a37f678baac5cf1902bd5eab5b8 -size 53240 +oid sha256:93a6d6723bc89b9660a440848461c1568004d312599d7d2d2b94299d6aa47c0a +size 57037 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png index df1ab02517..1e6522216a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.developer_DeveloperSettingsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42582ba2979c38cc0d2f8c3986b22c0dc31f8bc952e39f67669e46f5e42c6239 -size 51792 +oid sha256:bd7e58a29f370733af1e35531f428d5d2a732c70b9156d66ba590c9c5203f5d6 +size 55689 From d4f8052696f69521c86f13115a703d854ac325c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 19 Jul 2024 19:51:13 +0200 Subject: [PATCH 6/7] Unify the internal `pinunlock.SignOut` and the new `LogoutUseCase` classes --- features/lockscreen/impl/build.gradle.kts | 2 + .../impl/unlock/PinUnlockPresenter.kt | 6 +- .../impl/unlock/di/PinUnlockBindings.kt | 4 +- .../impl/unlock/signout/DefaultSignOut.kt | 40 ------------ .../impl/unlock/DefaultSignOutTest.kt | 61 ------------------- .../impl/unlock/PinUnlockPresenterTest.kt | 14 ++--- .../features/logout/api/LogoutUseCase.kt | 7 ++- .../logout/impl/DefaultLogoutUseCase.kt | 25 +++++--- .../logout/impl/SessionLogoutModule.kt | 36 +++++++++++ .../test/build.gradle.kts} | 16 +++-- .../logout/test/FakeLogoutUseCase.kt} | 18 +++--- features/preferences/impl/build.gradle.kts | 1 + .../DeveloperSettingsPresenterTest.kt | 21 +++---- 13 files changed, 103 insertions(+), 148 deletions(-) delete mode 100644 features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/DefaultSignOut.kt delete mode 100644 features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/DefaultSignOutTest.kt create mode 100644 features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt rename features/{lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/SignOut.kt => logout/test/build.gradle.kts} (64%) rename features/{lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/FakeSignOut.kt => logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt} (56%) diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index 21f3e05421..b8dc1c5035 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { implementation(projects.libraries.featureflag.api) implementation(projects.libraries.cryptography.api) implementation(projects.libraries.preferences.api) + implementation(projects.features.logout.api) implementation(projects.libraries.uiStrings) implementation(projects.libraries.sessionStorage.api) implementation(projects.services.appnavstate.api) @@ -59,4 +60,5 @@ dependencies { testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.services.appnavstate.test) + testImplementation(projects.features.logout.test) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index db56b8c17b..b563b3ac70 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -29,7 +29,7 @@ import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockMana import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel -import io.element.android.features.lockscreen.impl.unlock.signout.SignOut +import io.element.android.features.logout.api.LogoutUseCase import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -41,7 +41,7 @@ import javax.inject.Inject class PinUnlockPresenter @Inject constructor( private val pinCodeManager: PinCodeManager, private val biometricUnlockManager: BiometricUnlockManager, - private val signOut: SignOut, + private val logoutUseCase: LogoutUseCase, private val coroutineScope: CoroutineScope, private val pinUnlockHelper: PinUnlockHelper, ) : Presenter { @@ -179,7 +179,7 @@ class PinUnlockPresenter @Inject constructor( private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { suspend { - signOut() + logoutUseCase.logout(ignoreSdkError = true) }.runCatchingUpdatingState(signOutAction) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt index ddd62d2fb6..0f71222ebd 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt @@ -18,9 +18,9 @@ package io.element.android.features.lockscreen.impl.unlock.di import com.squareup.anvil.annotations.ContributesTo import io.element.android.features.lockscreen.impl.unlock.activity.PinUnlockActivity -import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SessionScope -@ContributesTo(AppScope::class) +@ContributesTo(SessionScope::class) interface PinUnlockBindings { fun inject(activity: PinUnlockActivity) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/DefaultSignOut.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/DefaultSignOut.kt deleted file mode 100644 index 2c541911e6..0000000000 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/DefaultSignOut.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2024 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.lockscreen.impl.unlock.signout - -import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.libraries.di.AppScope -import io.element.android.libraries.matrix.api.MatrixClientProvider -import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -class DefaultSignOut @Inject constructor( - private val authenticationService: MatrixAuthenticationService, - private val matrixClientProvider: MatrixClientProvider, -) : SignOut { - override suspend fun invoke(): String? { - val currentSession = authenticationService.getLatestSessionId() - return if (currentSession != null) { - matrixClientProvider.getOrRestore(currentSession) - .getOrThrow() - .logout(ignoreSdkError = true) - } else { - error("No session to sign out") - } - } -} diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/DefaultSignOutTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/DefaultSignOutTest.kt deleted file mode 100644 index 392693d9ef..0000000000 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/DefaultSignOutTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2024 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.lockscreen.impl.unlock - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.lockscreen.impl.unlock.signout.DefaultSignOut -import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.FakeMatrixClientProvider -import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService -import io.element.android.tests.testutils.lambda.assert -import io.element.android.tests.testutils.lambda.lambdaRecorder -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class DefaultSignOutTest { - private val matrixClient = FakeMatrixClient() - private val authenticationService = FakeMatrixAuthenticationService() - private val matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) - private val sut = DefaultSignOut(authenticationService, matrixClientProvider) - - @Test - fun `when no active session then it throws`() = runTest { - authenticationService.getLatestSessionIdLambda = { null } - val result = runCatching { sut.invoke() } - assertThat(result.isFailure).isTrue() - } - - @Test - fun `with one active session and successful logout on client`() = runTest { - val logoutLambda = lambdaRecorder { _: Boolean -> null } - authenticationService.getLatestSessionIdLambda = { matrixClient.sessionId } - matrixClient.logoutLambda = logoutLambda - val result = runCatching { sut.invoke() } - assertThat(result.isSuccess).isTrue() - assert(logoutLambda).isCalledOnce() - } - - @Test - fun `with one active session and and failed logout on client`() = runTest { - val logoutLambda = lambdaRecorder { _: Boolean -> error("Failed to logout") } - authenticationService.getLatestSessionIdLambda = { matrixClient.sessionId } - matrixClient.logoutLambda = logoutLambda - val result = runCatching { sut.invoke() } - assertThat(result.isFailure).isTrue() - assert(logoutLambda).isCalledOnce() - } -} diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index 89d0e92ee2..8299b5a1ab 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -28,7 +28,7 @@ import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.pin.model.assertText import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel -import io.element.android.features.lockscreen.impl.unlock.signout.SignOut +import io.element.android.features.logout.test.FakeLogoutUseCase import io.element.android.libraries.architecture.AsyncData import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -106,9 +106,9 @@ class PinUnlockPresenterTest { @Test fun `present - forgot pin flow`() = runTest { - val signOutLambda = lambdaRecorder { null } - val signOut = FakeSignOut(signOutLambda) - val presenter = createPinUnlockPresenter(this, signOut = signOut) + val signOutLambda = lambdaRecorder { "" } + val signOut = FakeLogoutUseCase(signOutLambda) + val presenter = createPinUnlockPresenter(this, logoutUseCase = signOut) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -135,7 +135,7 @@ class PinUnlockPresenterTest { awaitItem().also { state -> assertThat(state.signOutAction).isInstanceOf(AsyncData.Success::class.java) } - assert(signOutLambda).isCalledOnce().withNoParameter() + assert(signOutLambda).isCalledOnce() } } @@ -147,7 +147,7 @@ class PinUnlockPresenterTest { scope: CoroutineScope, biometricUnlockManager: BiometricUnlockManager = FakeBiometricUnlockManager(), callback: PinCodeManager.Callback = DefaultPinCodeManagerCallback(), - signOut: SignOut = FakeSignOut(), + logoutUseCase: FakeLogoutUseCase = FakeLogoutUseCase(logoutLambda = { "" }), ): PinUnlockPresenter { val pinCodeManager = aPinCodeManager().apply { addCallback(callback) @@ -156,7 +156,7 @@ class PinUnlockPresenterTest { return PinUnlockPresenter( pinCodeManager = pinCodeManager, biometricUnlockManager = biometricUnlockManager, - signOut = signOut, + logoutUseCase = logoutUseCase, coroutineScope = scope, pinUnlockHelper = PinUnlockHelper(biometricUnlockManager, pinCodeManager), ) diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt index 397f5ebed7..a06b7117b1 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt @@ -23,6 +23,11 @@ interface LogoutUseCase { /** * Log out the current user and then perform any needed cleanup tasks. * @param ignoreSdkError if true, the SDK error will be ignored and the user will be logged out anyway. + * @return the session id of the logged out user. */ - suspend fun logout(ignoreSdkError: Boolean) + suspend fun logout(ignoreSdkError: Boolean): String + + interface Factory { + fun create(sessionId: String): LogoutUseCase + } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt index 52bf0425d4..8e5f08a87a 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt @@ -17,16 +17,27 @@ package io.element.android.features.logout.impl import com.squareup.anvil.annotations.ContributesBinding +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import io.element.android.features.logout.api.LogoutUseCase -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.MatrixClient -import javax.inject.Inject +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.MatrixClientProvider +import io.element.android.libraries.matrix.api.core.SessionId -@ContributesBinding(SessionScope::class) -class DefaultLogoutUseCase @Inject constructor( - private val matrixClient: MatrixClient +class DefaultLogoutUseCase @AssistedInject constructor( + @Assisted private val sessionId: String, + private val matrixClientProvider: MatrixClientProvider, ) : LogoutUseCase { - override suspend fun logout(ignoreSdkError: Boolean) { + @ContributesBinding(AppScope::class) + @AssistedFactory + interface Factory : LogoutUseCase.Factory { + override fun create(sessionId: String): DefaultLogoutUseCase + } + + override suspend fun logout(ignoreSdkError: Boolean): String { + val matrixClient = matrixClientProvider.getOrRestore(SessionId(sessionId)).getOrThrow() matrixClient.logout(ignoreSdkError = ignoreSdkError) + return sessionId } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt new file mode 100644 index 0000000000..ec725a34a6 --- /dev/null +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 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 + * + * https://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.logout.impl + +import com.squareup.anvil.annotations.ContributesTo +import dagger.Module +import dagger.Provides +import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder + +@Module +@ContributesTo(SessionScope::class) +object SessionLogoutModule { + @Provides + fun provideLogoutUseCase( + currentSessionIdHolder: CurrentSessionIdHolder, + factory: DefaultLogoutUseCase.Factory, + ): LogoutUseCase { + return factory.create(currentSessionIdHolder.current.value) + } +} diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/SignOut.kt b/features/logout/test/build.gradle.kts similarity index 64% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/SignOut.kt rename to features/logout/test/build.gradle.kts index f4c91aece6..7be20ef279 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/signout/SignOut.kt +++ b/features/logout/test/build.gradle.kts @@ -5,7 +5,7 @@ * 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 + * https://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, @@ -14,8 +14,16 @@ * limitations under the License. */ -package io.element.android.features.lockscreen.impl.unlock.signout +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.logout.test" +} -interface SignOut { - suspend operator fun invoke(): String? +dependencies { + implementation(libs.coroutines.core) + implementation(projects.tests.testutils) + api(projects.features.logout.api) } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/FakeSignOut.kt b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt similarity index 56% rename from features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/FakeSignOut.kt rename to features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt index 883a5bf97b..bbc67765d1 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/FakeSignOut.kt +++ b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt @@ -5,7 +5,7 @@ * 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 + * https://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, @@ -14,15 +14,15 @@ * limitations under the License. */ -package io.element.android.features.lockscreen.impl.unlock +package io.element.android.features.logout.test -import io.element.android.features.lockscreen.impl.unlock.signout.SignOut -import io.element.android.tests.testutils.simulateLongTask +import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.tests.testutils.lambda.lambdaError -class FakeSignOut( - var lambda: () -> String? = { null } -) : SignOut { - override suspend fun invoke(): String? = simulateLongTask { - lambda() +class FakeLogoutUseCase( + var logoutLambda: (Boolean) -> String = lambdaError() +) : LogoutUseCase { + override suspend fun logout(ignoreSdkError: Boolean): String { + return logoutLambda(ignoreSdkError) } } diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index ead4515360..ec265d739e 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -90,6 +90,7 @@ dependencies { testImplementation(projects.features.ftue.test) testImplementation(projects.features.rageshake.test) testImplementation(projects.features.rageshake.impl) + testImplementation(projects.features.logout.test) testImplementation(projects.features.roomlist.test) testImplementation(projects.libraries.indicator.impl) testImplementation(projects.libraries.pushproviders.test) 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 be3c6bbb20..9fb1c8b37f 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 @@ -21,7 +21,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.appconfig.ElementCallConfig -import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.features.logout.test.FakeLogoutUseCase import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter @@ -36,6 +36,7 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem +import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -165,7 +166,8 @@ class DeveloperSettingsPresenterTest { @Test fun `present - toggling simplified sliding sync changes the preferences and logs out the user`() = runTest { - val logoutUseCase = FakeLogoutUseCase() + val logoutCallRecorder = lambdaRecorder { "" } + val logoutUseCase = FakeLogoutUseCase(logoutLambda = logoutCallRecorder) val preferences = InMemoryAppPreferencesStore() val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences, logoutUseCase = logoutUseCase) moleculeFlow(RecompositionMode.Immediate) { @@ -177,12 +179,12 @@ class DeveloperSettingsPresenterTest { initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(true)) assertThat(awaitItem().isSimpleSlidingSyncEnabled).isTrue() assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isTrue() - assertThat(logoutUseCase.logoutCallCount).isEqualTo(1) + logoutCallRecorder.assertions().isCalledOnce() initialState.eventSink(DeveloperSettingsEvents.SetSimplifiedSlidingSyncEnabled(false)) assertThat(awaitItem().isSimpleSlidingSyncEnabled).isFalse() assertThat(preferences.isSimplifiedSlidingSyncEnabledFlow().first()).isFalse() - assertThat(logoutUseCase.logoutCallCount).isEqualTo(2) + logoutCallRecorder.assertions().isCalledExactly(times = 2) } } @@ -193,7 +195,7 @@ class DeveloperSettingsPresenterTest { rageshakePresenter: DefaultRageshakePreferencesPresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore()), preferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(), buildMeta: BuildMeta = aBuildMeta(), - logoutUseCase: FakeLogoutUseCase = FakeLogoutUseCase() + logoutUseCase: FakeLogoutUseCase = FakeLogoutUseCase(logoutLambda = { "" }) ): DeveloperSettingsPresenter { return DeveloperSettingsPresenter( featureFlagService = featureFlagService, @@ -206,12 +208,3 @@ class DeveloperSettingsPresenterTest { ) } } - -private class FakeLogoutUseCase : LogoutUseCase { - var logoutCallCount = 0 - private set - - override suspend fun logout(ignoreSdkError: Boolean) { - logoutCallCount++ - } -} From eec0e371f60ea012b062f1885cde4b0a0dbaf5dc Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 24 Jul 2024 18:32:26 +0200 Subject: [PATCH 7/7] SSS : enhance getBaseClientBuilder to avoid mistakes. --- .../matrix/impl/RustMatrixClientFactory.kt | 27 ++++++++++++++++--- .../auth/RustMatrixAuthenticationService.kt | 4 ++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index 96d8e4939d..51e71f3855 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -54,7 +54,11 @@ class RustMatrixClientFactory @Inject constructor( val client = getBaseClientBuilder( sessionPath = sessionData.sessionPath, passphrase = sessionData.passphrase, - useSimplifiedSlidingSync = appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first(), + slidingSync = if (appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()) { + ClientBuilderSlidingSync.Simplified + } else { + ClientBuilderSlidingSync.Restored + }, ) .homeserverUrl(sessionData.homeserverUrl) .username(sessionData.userId) @@ -86,7 +90,7 @@ class RustMatrixClientFactory @Inject constructor( sessionPath: String, passphrase: String?, slidingSyncProxy: String? = null, - useSimplifiedSlidingSync: Boolean = false, + slidingSync: ClientBuilderSlidingSync, ): ClientBuilder { return ClientBuilder() .sessionPath(sessionPath) @@ -96,7 +100,13 @@ class RustMatrixClientFactory @Inject constructor( .addRootCertificates(userCertificatesProvider.provides()) .autoEnableBackups(true) .autoEnableCrossSigning(true) - .simplifiedSlidingSync(useSimplifiedSlidingSync) + .run { + when (slidingSync) { + ClientBuilderSlidingSync.Restored -> this + ClientBuilderSlidingSync.Discovered -> requiresSlidingSync() + ClientBuilderSlidingSync.Simplified -> simplifiedSlidingSync(true) + } + } .run { // Workaround for non-nullable proxy parameter in the SDK, since each call to the ClientBuilder returns a new reference we need to keep proxyProvider.provides()?.let { proxy(it) } ?: this @@ -104,6 +114,17 @@ class RustMatrixClientFactory @Inject constructor( } } +enum class ClientBuilderSlidingSync { + // The proxy will be supplied when restoring the Session. + Restored, + + // A proxy must be discovered whilst building the session. + Discovered, + + // Use Simplified Sliding Sync (discovery isn't a thing yet). + Simplified, +} + private fun SessionData.toSession() = Session( accessToken = accessToken, refreshToken = refreshToken, 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 8c0546fa8b..e1ff0811e1 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 @@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData @@ -210,6 +211,7 @@ class RustMatrixAuthenticationService @Inject constructor( sessionPath = sessionPath, passphrase = pendingPassphrase, slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL, + slidingSync = ClientBuilderSlidingSync.Discovered, ) .buildWithQrCode( qrCodeData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData, @@ -251,8 +253,8 @@ class RustMatrixAuthenticationService @Inject constructor( sessionPath = sessionPath, passphrase = pendingPassphrase, slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL, + slidingSync = ClientBuilderSlidingSync.Discovered, ) - .requiresSlidingSync() private fun clear() { currentClient?.close()