From 9ca5fbdc08a4c0505360b2c6578ec31466cb5332 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:20:15 +0200 Subject: [PATCH 01/10] Add parameter `wasLastSession` to `SessionListener.onSessionDeleted` --- .../android/features/invite/impl/DefaultSeenInvitesStore.kt | 2 +- .../features/lockscreen/impl/DefaultLockScreenService.kt | 2 +- .../android/libraries/matrix/ui/media/ImageLoaderHolder.kt | 2 +- .../impl/store/DefaultSessionPreferencesStoreFactory.kt | 2 +- .../element/android/libraries/push/impl/DefaultPushService.kt | 2 +- .../conversations/DefaultNotificationConversationService.kt | 2 +- .../android/libraries/push/impl/DefaultPushServiceTest.kt | 4 ++-- .../libraries/sessionstorage/api/observer/SessionListener.kt | 2 +- .../sessionstorage/impl/observer/DefaultSessionObserver.kt | 3 ++- .../sessionstorage/impl/observer/TestSessionListener.kt | 2 +- .../sessionstorage/test/observer/FakeSessionObserver.kt | 2 +- .../services/analytics/impl/DefaultAnalyticsService.kt | 2 +- .../services/analytics/impl/DefaultAnalyticsServiceTest.kt | 2 +- 13 files changed, 15 insertions(+), 14 deletions(-) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt index 38295daa27..5a8a9116d5 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt @@ -34,7 +34,7 @@ class DefaultSeenInvitesStore( init { sessionObserver.addListener(object : SessionListener { override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { if (sessionId.value == userId) { clear() } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt index 8995eee65a..03ac11e086 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt @@ -79,7 +79,7 @@ class DefaultLockScreenService( sessionObserver.addListener(object : SessionListener { override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { // TODO handle multi session at some point pinCodeManager.deletePinCode() } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt index 16cb3cca18..77d3abffef 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt @@ -37,7 +37,7 @@ class DefaultImageLoaderHolder( sessionObserver.addListener(object : SessionListener { override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { remove(SessionId(userId)) } }) diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt index fefbd84104..07df852208 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt @@ -31,7 +31,7 @@ class DefaultSessionPreferencesStoreFactory( init { sessionObserver.addListener(object : SessionListener { override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { val sessionPreferences = cache.remove(SessionId(userId)) sessionPreferences?.clear() } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt index c92e512a71..8e8874d6ea 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt @@ -119,7 +119,7 @@ class DefaultPushService( * The current push provider may want to take action, and we need to * cleanup the stores. */ - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { val sessionId = SessionId(userId) val userPushStore = userPushStoreFactory.getOrCreate(sessionId) val currentPushProviderName = userPushStore.getPushProviderName() diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt index f61487c571..43824c88ae 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt @@ -61,7 +61,7 @@ class DefaultNotificationConversationService( sessionObserver.addListener(object : SessionListener { override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { onSessionLogOut(SessionId(userId)) } }) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt index 5ae8ab261e..dffd7d4846 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPushServiceTest.kt @@ -248,7 +248,7 @@ class DefaultPushServiceTest { ), pushClientSecretStore = pushClientSecretStore, ) - defaultPushService.onSessionDeleted(A_SESSION_ID.value) + defaultPushService.onSessionDeleted(A_SESSION_ID.value, false) assertThat(userPushStore.getPushProviderName()).isNull() assertThat(pushClientSecretStore.getSecret(A_SESSION_ID)).isNull() onSessionDeletedLambda.assertions().isCalledOnce().with(value(A_SESSION_ID)) @@ -268,7 +268,7 @@ class DefaultPushServiceTest { ), pushClientSecretStore = pushClientSecretStore, ) - defaultPushService.onSessionDeleted(A_SESSION_ID.value) + defaultPushService.onSessionDeleted(A_SESSION_ID.value, false) assertThat(userPushStore.getPushProviderName()).isNull() assertThat(pushClientSecretStore.getSecret(A_SESSION_ID)).isNull() } diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt index b0db9fa4bc..6066ffd09b 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt @@ -9,5 +9,5 @@ package io.element.android.libraries.sessionstorage.api.observer interface SessionListener { suspend fun onSessionCreated(userId: String) - suspend fun onSessionDeleted(userId: String) + suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) } diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserver.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserver.kt index 71a63db82d..be2e5c9eaa 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserver.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserver.kt @@ -60,9 +60,10 @@ class DefaultSessionObserver( // Compute diff // Removed user val removedUsers = currentUserSet - newUserSet + val wasLastSession = newUserSet.isEmpty() removedUsers.forEach { removedUser -> listeners.onEach { listener -> - listener.onSessionDeleted(removedUser) + listener.onSessionDeleted(removedUser, wasLastSession) } } // Added user diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt index 91ae519538..b08c7ffa6e 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt @@ -22,7 +22,7 @@ class TestSessionListener : SessionListener { trackRecord.add(Event.Created(userId)) } - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { trackRecord.add(Event.Deleted(userId)) } diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt index 817046517a..ef11f5d2d3 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt @@ -29,6 +29,6 @@ class FakeSessionObserver : SessionObserver { } suspend fun onSessionDeleted(userId: String) { - listeners.forEach { it.onSessionDeleted(userId) } + listeners.forEach { it.onSessionDeleted(userId, false) } } } diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt index 7d06f762e5..dc6b32ef7d 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt @@ -79,7 +79,7 @@ class DefaultAnalyticsService( // Nothing to do } - override suspend fun onSessionDeleted(userId: String) { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { // Delete the store when the last session is deleted if (sessionStore.getAllSessions().isEmpty()) { analyticsStore.reset() diff --git a/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt b/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt index 1e5a54fb26..61950d122e 100644 --- a/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt +++ b/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt @@ -178,7 +178,7 @@ class DefaultAnalyticsServiceTest { coroutineScope = backgroundScope, analyticsStore = store, ) - sut.onSessionDeleted("userId") + sut.onSessionDeleted("userId", false) resetLambda.assertions().isCalledOnce() } From a18eb6eb5edf72f5d88c739bd7fc3015b42dc83b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:23:01 +0200 Subject: [PATCH 02/10] Add default implementation to `SessionListener` --- .../android/features/invite/impl/DefaultSeenInvitesStore.kt | 1 - .../features/lockscreen/impl/DefaultLockScreenService.kt | 2 -- .../android/libraries/matrix/ui/media/ImageLoaderHolder.kt | 2 -- .../impl/store/DefaultSessionPreferencesStoreFactory.kt | 1 - .../element/android/libraries/push/impl/DefaultPushService.kt | 4 ---- .../conversations/DefaultNotificationConversationService.kt | 2 -- .../libraries/sessionstorage/api/observer/SessionListener.kt | 4 ++-- .../services/analytics/impl/DefaultAnalyticsService.kt | 4 ---- 8 files changed, 2 insertions(+), 18 deletions(-) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt index 5a8a9116d5..c358cbf36f 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/DefaultSeenInvitesStore.kt @@ -33,7 +33,6 @@ class DefaultSeenInvitesStore( ) : SeenInvitesStore { init { sessionObserver.addListener(object : SessionListener { - override suspend fun onSessionCreated(userId: String) = Unit override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { if (sessionId.value == userId) { clear() diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt index 03ac11e086..ffdb4e67ff 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt @@ -77,8 +77,6 @@ class DefaultLockScreenService( */ private fun observeSessionsState() { sessionObserver.addListener(object : SessionListener { - override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { // TODO handle multi session at some point pinCodeManager.deletePinCode() diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt index 77d3abffef..12cc099eee 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt @@ -35,8 +35,6 @@ class DefaultImageLoaderHolder( private fun observeSessions() { sessionObserver.addListener(object : SessionListener { - override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { remove(SessionId(userId)) } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt index 07df852208..e2e8c2088a 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultSessionPreferencesStoreFactory.kt @@ -30,7 +30,6 @@ class DefaultSessionPreferencesStoreFactory( init { sessionObserver.addListener(object : SessionListener { - override suspend fun onSessionCreated(userId: String) = Unit override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { val sessionPreferences = cache.remove(SessionId(userId)) sessionPreferences?.clear() diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt index 8e8874d6ea..f872754827 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPushService.kt @@ -108,10 +108,6 @@ class DefaultPushService( sessionObserver.addListener(this) } - override suspend fun onSessionCreated(userId: String) { - // Nothing to do - } - /** * The session has been deleted. * In this case, this is not necessary to unregister the pusher from the homeserver, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt index 43824c88ae..ea282caa6d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt @@ -59,8 +59,6 @@ class DefaultNotificationConversationService( init { sessionObserver.addListener(object : SessionListener { - override suspend fun onSessionCreated(userId: String) = Unit - override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { onSessionLogOut(SessionId(userId)) } diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt index 6066ffd09b..fa6226a5ec 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt @@ -8,6 +8,6 @@ package io.element.android.libraries.sessionstorage.api.observer interface SessionListener { - suspend fun onSessionCreated(userId: String) - suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) + suspend fun onSessionCreated(userId: String) = Unit + suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) = Unit } diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt index dc6b32ef7d..656373b3a6 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt @@ -75,10 +75,6 @@ class DefaultAnalyticsService( analyticsStore.setAnalyticsId(analyticsId) } - override suspend fun onSessionCreated(userId: String) { - // Nothing to do - } - override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { // Delete the store when the last session is deleted if (sessionStore.getAllSessions().isEmpty()) { From e23f9c31c53f73856e2c3e5c8502f57ca9f72c59 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:32:19 +0200 Subject: [PATCH 03/10] Rename fixture fun for clarity. --- .../sessionstorage/impl/DatabaseSessionStoreTest.kt | 2 +- .../android/libraries/sessionstorage/impl/Fixtures.kt | 2 +- .../impl/observer/DefaultSessionObserverTest.kt | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt index 7d264f42db..d1fef8a39a 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt @@ -24,7 +24,7 @@ class DatabaseSessionStoreTest { private lateinit var database: SessionDatabase private lateinit var databaseSessionStore: DatabaseSessionStore - private val aSessionData = aSessionData() + private val aSessionData = aDbSessionData() @OptIn(ExperimentalCoroutinesApi::class) @Before diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt index e8713dac1a..165a91b5b5 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.sessionstorage.impl import io.element.android.libraries.matrix.session.SessionData import io.element.android.libraries.sessionstorage.api.LoginType -internal fun aSessionData() = SessionData( +internal fun aDbSessionData() = SessionData( userId = "userId", deviceId = "deviceId", accessToken = "accessToken", diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt index 8b0184fdc7..f833f146a4 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt @@ -11,7 +11,7 @@ import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.sessionstorage.impl.DatabaseSessionStore import io.element.android.libraries.sessionstorage.impl.SessionDatabase -import io.element.android.libraries.sessionstorage.impl.aSessionData +import io.element.android.libraries.sessionstorage.impl.aDbSessionData import io.element.android.libraries.sessionstorage.impl.toApiModel import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -46,7 +46,7 @@ import org.junit.Test @Test fun `adding data invokes onSessionCreated`() = runTest { - val sessionData = aSessionData() + val sessionData = aDbSessionData() val sut = createDefaultSessionObserver() runCurrent() val listener = TestSessionListener() @@ -59,7 +59,7 @@ import org.junit.Test @Test fun `adding and deleting data invokes onSessionCreated and onSessionDeleted`() = runTest { - val sessionData = aSessionData() + val sessionData = aDbSessionData() val sut = createDefaultSessionObserver() runCurrent() val listener = TestSessionListener() From 11a808a2e0c33eaae80814440b1ccd4e54a3c49e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:37:16 +0200 Subject: [PATCH 04/10] Add test on `wasLastSession` value. --- .../libraries/sessionstorage/impl/Fixtures.kt | 6 +++-- .../observer/DefaultSessionObserverTest.kt | 26 +++++++++++++++++-- .../impl/observer/TestSessionListener.kt | 4 +-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt index 165a91b5b5..2fd9900bdf 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt @@ -10,8 +10,10 @@ package io.element.android.libraries.sessionstorage.impl import io.element.android.libraries.matrix.session.SessionData import io.element.android.libraries.sessionstorage.api.LoginType -internal fun aDbSessionData() = SessionData( - userId = "userId", +internal fun aDbSessionData( + userId: String = "userId", +) = SessionData( + userId = userId, deviceId = "deviceId", accessToken = "accessToken", refreshToken = "refreshToken", diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt index f833f146a4..e9910cbe77 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt @@ -23,7 +23,8 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class DefaultSessionObserverTest { +@OptIn(ExperimentalCoroutinesApi::class) +class DefaultSessionObserverTest { private lateinit var database: SessionDatabase private lateinit var databaseSessionStore: DatabaseSessionStore @@ -69,7 +70,28 @@ import org.junit.Test databaseSessionStore.removeSession(sessionData.userId) listener.assertEvents( TestSessionListener.Event.Created(sessionData.userId), - TestSessionListener.Event.Deleted(sessionData.userId), + TestSessionListener.Event.Deleted(sessionData.userId, true), + ) + coroutineContext.cancelChildren() + } + + @Test + fun `adding and deleting data twice invokes onSessionCreated and onSessionDeleted`() = runTest { + val sessionData1 = aDbSessionData(userId = "user1") + val sessionData2 = aDbSessionData(userId = "user2") + val sut = createDefaultSessionObserver() + runCurrent() + val listener = TestSessionListener() + sut.addListener(listener) + databaseSessionStore.addSession(sessionData1.toApiModel()) + databaseSessionStore.addSession(sessionData2.toApiModel()) + databaseSessionStore.removeSession(sessionData2.userId) + databaseSessionStore.removeSession(sessionData1.userId) + listener.assertEvents( + TestSessionListener.Event.Created(sessionData1.userId), + TestSessionListener.Event.Created(sessionData2.userId), + TestSessionListener.Event.Deleted(sessionData2.userId, wasLastSession = false), + TestSessionListener.Event.Deleted(sessionData1.userId, wasLastSession = true), ) coroutineContext.cancelChildren() } diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt index b08c7ffa6e..87bebf63b5 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.sessionstorage.api.observer.SessionListener class TestSessionListener : SessionListener { sealed interface Event { data class Created(val userId: String) : Event - data class Deleted(val userId: String) : Event + data class Deleted(val userId: String, val wasLastSession: Boolean) : Event } private val trackRecord: MutableList = mutableListOf() @@ -23,7 +23,7 @@ class TestSessionListener : SessionListener { } override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { - trackRecord.add(Event.Deleted(userId)) + trackRecord.add(Event.Deleted(userId, wasLastSession)) } fun assertEvents(vararg events: Event) { From 34a5785f38d387cc8be0b9c1fdcab1bd495f5255 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:38:30 +0200 Subject: [PATCH 05/10] Use TestScope.backgroundScope --- .../impl/observer/DefaultSessionObserverTest.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt index e9910cbe77..a2ed815b88 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt @@ -15,7 +15,6 @@ import io.element.android.libraries.sessionstorage.impl.aDbSessionData import io.element.android.libraries.sessionstorage.impl.toApiModel import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent @@ -55,7 +54,6 @@ class DefaultSessionObserverTest { databaseSessionStore.addSession(sessionData.toApiModel()) listener.assertEvents(TestSessionListener.Event.Created(sessionData.userId)) sut.removeListener(listener) - coroutineContext.cancelChildren() } @Test @@ -72,7 +70,6 @@ class DefaultSessionObserverTest { TestSessionListener.Event.Created(sessionData.userId), TestSessionListener.Event.Deleted(sessionData.userId, true), ) - coroutineContext.cancelChildren() } @Test @@ -93,13 +90,12 @@ class DefaultSessionObserverTest { TestSessionListener.Event.Deleted(sessionData2.userId, wasLastSession = false), TestSessionListener.Event.Deleted(sessionData1.userId, wasLastSession = true), ) - coroutineContext.cancelChildren() } private fun TestScope.createDefaultSessionObserver(): DefaultSessionObserver { return DefaultSessionObserver( sessionStore = databaseSessionStore, - coroutineScope = this, + coroutineScope = backgroundScope, dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), ) } From 7777607a37d39b19cea7fb5ac64a4c0e728e1573 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:44:14 +0200 Subject: [PATCH 06/10] Use parameter `wasLastSession` --- .../analytics/impl/DefaultAnalyticsService.kt | 4 +--- .../impl/DefaultAnalyticsServiceTest.kt | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt index 656373b3a6..f7f92e25ce 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt @@ -16,7 +16,6 @@ import im.vector.app.features.analytics.itf.VectorAnalyticsScreen import im.vector.app.features.analytics.plan.SuperProperties import im.vector.app.features.analytics.plan.UserProperties import io.element.android.libraries.di.annotations.AppCoroutineScope -import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.services.analytics.api.AnalyticsService @@ -39,7 +38,6 @@ class DefaultAnalyticsService( @AppCoroutineScope private val coroutineScope: CoroutineScope, private val sessionObserver: SessionObserver, - private val sessionStore: SessionStore, ) : AnalyticsService, SessionListener { // Cache for the store values private val userConsent = AtomicBoolean(false) @@ -77,7 +75,7 @@ class DefaultAnalyticsService( override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { // Delete the store when the last session is deleted - if (sessionStore.getAllSessions().isEmpty()) { + if (wasLastSession) { analyticsStore.reset() } } diff --git a/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt b/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt index 61950d122e..c2594feaa4 100644 --- a/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt +++ b/services/analytics/impl/src/test/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsServiceTest.kt @@ -16,9 +16,7 @@ import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.PollEnd import im.vector.app.features.analytics.plan.SuperProperties import im.vector.app.features.analytics.plan.UserProperties -import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.observer.SessionObserver -import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver import io.element.android.services.analytics.impl.store.AnalyticsStore import io.element.android.services.analytics.impl.store.FakeAnalyticsStore @@ -178,10 +176,24 @@ class DefaultAnalyticsServiceTest { coroutineScope = backgroundScope, analyticsStore = store, ) - sut.onSessionDeleted("userId", false) + sut.onSessionDeleted("userId", true) resetLambda.assertions().isCalledOnce() } + @Test + fun `when a session is deleted, the store is not reset if it was not the last session`() = runTest { + val resetLambda = lambdaRecorder { } + val store = FakeAnalyticsStore( + resetLambda = resetLambda, + ) + val sut = createDefaultAnalyticsService( + coroutineScope = backgroundScope, + analyticsStore = store, + ) + sut.onSessionDeleted("userId", false) + resetLambda.assertions().isNeverCalled() + } + @Test fun `when a session is added, nothing happen`() = runTest { val sut = createDefaultAnalyticsService( @@ -260,13 +272,11 @@ class DefaultAnalyticsServiceTest { ), analyticsStore: AnalyticsStore = FakeAnalyticsStore(), sessionObserver: SessionObserver = NoOpSessionObserver(), - sessionStore: SessionStore = InMemorySessionStore(), ) = DefaultAnalyticsService( analyticsProviders = analyticsProviders, analyticsStore = analyticsStore, coroutineScope = coroutineScope, sessionObserver = sessionObserver, - sessionStore = sessionStore, ).also { // Wait for the service to be ready delay(1) From 0284fd22e8a3550296560f2341efa0cec0b3aa1d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:46:33 +0200 Subject: [PATCH 07/10] Delete the PIN code only when the last session is deleted. --- .../features/lockscreen/impl/DefaultLockScreenService.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt index ffdb4e67ff..77e190b64d 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt @@ -73,13 +73,14 @@ class DefaultLockScreenService( } /** - * Makes sure to delete the pin code when the session is deleted. + * Makes sure to delete the pin code when the last session is deleted. */ private fun observeSessionsState() { sessionObserver.addListener(object : SessionListener { override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { - // TODO handle multi session at some point - pinCodeManager.deletePinCode() + if (wasLastSession) { + pinCodeManager.deletePinCode() + } } }) } From 7cea6f0f8f8c21806bdab8e00db2051fb3f4fac4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 16:52:24 +0200 Subject: [PATCH 08/10] Remove dead code. --- .../android/features/lockscreen/api/LockScreenService.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt index 4919442e28..987bd75166 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt @@ -35,12 +35,6 @@ interface LockScreenService { fun isPinSetup(): Flow } -/** - * Check if the app is currently locked. - */ -val LockScreenService.isLocked: Boolean - get() = lockState.value == LockScreenLockState.Locked - /** * Makes sure the secure flag is set on the activity if the pin is setup. * @param activity the activity to set the flag on. From 85035da028604af67340e8f03cda554798b79949 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 17:12:11 +0200 Subject: [PATCH 09/10] Add unit test on DefaultLockScreenService --- .../impl/DefaultLockScreenServiceTest.kt | 95 +++++++++++++++++++ .../impl/pin/DefaultPinCodeManagerTest.kt | 21 +++- .../test/observer/FakeSessionObserver.kt | 4 +- 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt new file mode 100644 index 0000000000..4672a82bf4 --- /dev/null +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.lockscreen.impl + +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager +import io.element.android.features.lockscreen.impl.biometric.FakeBiometricAuthenticatorManager +import io.element.android.features.lockscreen.impl.fixtures.aLockScreenConfig +import io.element.android.features.lockscreen.impl.pin.PinCodeManager +import io.element.android.features.lockscreen.impl.pin.createDefaultPinCodeManager +import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore +import io.element.android.features.lockscreen.impl.storage.LockScreenStore +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver +import io.element.android.services.appnavstate.api.AppForegroundStateService +import io.element.android.services.appnavstate.test.FakeAppForegroundStateService +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultLockScreenServiceTest { + @Test + fun `when the pin is not mandatory and no pin is configured isSetupRequired emits false`() = runTest { + val sut = createDefaultLockScreenService( + lockScreenConfig = aLockScreenConfig(isPinMandatory = false) + ) + sut.isSetupRequired().test { + assertThat(awaitItem()).isFalse() + } + } + + @Test + fun `when the pin is mandatory, isSetupRequired emits true`() = runTest { + val lockScreenStore = InMemoryLockScreenStore() + val sut = createDefaultLockScreenService( + lockScreenConfig = aLockScreenConfig(isPinMandatory = true), + lockScreenStore = lockScreenStore, + ) + sut.isSetupRequired().test { + assertThat(awaitItem()).isTrue() + // When the user configures the pin code, the setup is not required anymore + lockScreenStore.saveEncryptedPinCode("encryptedCode") + assertThat(awaitItem()).isFalse() + // Users deletes the pin code + lockScreenStore.deleteEncryptedPinCode() + assertThat(awaitItem()).isTrue() + } + } + + @Test + fun `when the last session is deleted, the pin code is removed`() = runTest { + val sessionObserver = FakeSessionObserver() + val lockScreenStore = InMemoryLockScreenStore() + val sut = createDefaultLockScreenService( + lockScreenConfig = aLockScreenConfig(isPinMandatory = true), + lockScreenStore = lockScreenStore, + sessionObserver = sessionObserver, + ) + sut.isPinSetup().test { + assertThat(awaitItem()).isFalse() + // When the user configure the pin code, the setup is not required anymore + lockScreenStore.saveEncryptedPinCode("encryptedCode") + assertThat(awaitItem()).isTrue() + sessionObserver.onSessionDeleted("userId", wasLastSession = false) + expectNoEvents() + sessionObserver.onSessionDeleted("userId", wasLastSession = true) + assertThat(awaitItem()).isFalse() + } + } +} + +private fun TestScope.createDefaultLockScreenService( + lockScreenConfig: LockScreenConfig = aLockScreenConfig(), + lockScreenStore: LockScreenStore = InMemoryLockScreenStore(), + pinCodeManager: PinCodeManager = createDefaultPinCodeManager( + lockScreenStore = lockScreenStore, + ), + sessionObserver: SessionObserver = FakeSessionObserver(), + appForegroundStateService: AppForegroundStateService = FakeAppForegroundStateService(), + biometricAuthenticatorManager: BiometricAuthenticatorManager = FakeBiometricAuthenticatorManager(), +) = DefaultLockScreenService( + lockScreenConfig = lockScreenConfig, + lockScreenStore = lockScreenStore, + pinCodeManager = pinCodeManager, + coroutineScope = backgroundScope, + sessionObserver = sessionObserver, + appForegroundStateService = appForegroundStateService, + biometricAuthenticatorManager = biometricAuthenticatorManager, +) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt index ed942c45b9..5c29c7ecea 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManagerTest.kt @@ -10,19 +10,18 @@ package io.element.android.features.lockscreen.impl.pin import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore +import io.element.android.features.lockscreen.impl.storage.LockScreenStore +import io.element.android.libraries.cryptography.api.EncryptionDecryptionService +import io.element.android.libraries.cryptography.api.SecretKeyRepository import io.element.android.libraries.cryptography.impl.AESEncryptionDecryptionService import io.element.android.libraries.cryptography.test.SimpleSecretKeyRepository import kotlinx.coroutines.test.runTest import org.junit.Test class DefaultPinCodeManagerTest { - private val lockScreenStore = InMemoryLockScreenStore() - private val secretKeyRepository = SimpleSecretKeyRepository() - private val encryptionDecryptionService = AESEncryptionDecryptionService() - private val pinCodeManager = DefaultPinCodeManager(secretKeyRepository, encryptionDecryptionService, lockScreenStore) - @Test fun `given a pin code when create and delete assert no pin code left`() = runTest { + val pinCodeManager = createDefaultPinCodeManager() pinCodeManager.hasPinCode().test { assertThat(awaitItem()).isFalse() pinCodeManager.createPinCode("1234") @@ -34,6 +33,7 @@ class DefaultPinCodeManagerTest { @Test fun `given a pin code when create and verify with the same pin succeed`() = runTest { + val pinCodeManager = createDefaultPinCodeManager() val pinCode = "1234" pinCodeManager.createPinCode(pinCode) assertThat(pinCodeManager.verifyPinCode(pinCode)).isTrue() @@ -41,7 +41,18 @@ class DefaultPinCodeManagerTest { @Test fun `given a pin code when create and verify with a different pin fails`() = runTest { + val pinCodeManager = createDefaultPinCodeManager() pinCodeManager.createPinCode("1234") assertThat(pinCodeManager.verifyPinCode("1235")).isFalse() } } + +fun createDefaultPinCodeManager( + lockScreenStore: LockScreenStore = InMemoryLockScreenStore(), + secretKeyRepository: SecretKeyRepository = SimpleSecretKeyRepository(), + encryptionDecryptionService: EncryptionDecryptionService = AESEncryptionDecryptionService(), +) = DefaultPinCodeManager( + lockScreenStore = lockScreenStore, + secretKeyRepository = secretKeyRepository, + encryptionDecryptionService = encryptionDecryptionService, +) diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt index ef11f5d2d3..eb34a713bb 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt @@ -28,7 +28,7 @@ class FakeSessionObserver : SessionObserver { listeners.forEach { it.onSessionCreated(userId) } } - suspend fun onSessionDeleted(userId: String) { - listeners.forEach { it.onSessionDeleted(userId, false) } + suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean = true) { + listeners.forEach { it.onSessionDeleted(userId, wasLastSession = wasLastSession) } } } From 752a4ea8462fb994ea315851e05657e865717de1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Oct 2025 18:03:22 +0200 Subject: [PATCH 10/10] Fix detekt issue. --- .../libraries/sessionstorage/api/observer/SessionListener.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt index fa6226a5ec..0a5234f725 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/observer/SessionListener.kt @@ -8,6 +8,6 @@ package io.element.android.libraries.sessionstorage.api.observer interface SessionListener { - suspend fun onSessionCreated(userId: String) = Unit - suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) = Unit + suspend fun onSessionCreated(userId: String) {} + suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) {} }