From 790b9cff638488abe7c97eb6a0597c6eb14728c8 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:34:38 +0300 Subject: [PATCH] Hide message notification content when the app is locked (#6902) * Hide message notification content by default when the app is locked * Make this the default without an option * Fix test * Update DefaultNotificationDrawerManager.kt * Fix --- .../DefaultNotificationDrawerManager.kt | 51 ++++++++++++++++++- .../notifications/RoomGroupMessageCreator.kt | 2 +- .../DefaultNotificationDrawerManagerTest.kt | 4 ++ .../fake/FakeNotificationCreator.kt | 12 ++++- .../pushstore/impl/UserPushStoreDataStore.kt | 1 - 5 files changed, 66 insertions(+), 4 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index e328c46209..b6d15dac4e 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.push.impl.notifications import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn +import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId @@ -37,6 +38,7 @@ import io.element.android.services.appnavstate.api.currentRoomId import io.element.android.services.appnavstate.api.currentSessionId import io.element.android.services.appnavstate.api.currentThreadId import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch /** @@ -55,6 +57,7 @@ class DefaultNotificationDrawerManager( private val matrixClientProvider: MatrixClientProvider, private val imageLoaderHolder: ImageLoaderHolder, private val activeNotificationsProvider: ActiveNotificationsProvider, + private val lockScreenService: LockScreenService, sessionObserver: SessionObserver, ) : NotificationCleaner { // TODO EAx add a setting per user for this @@ -197,6 +200,8 @@ class DefaultNotificationDrawerManager( it.sessionId } + val isAppLocked = lockScreenService.isPinSetup().first() + for ((sessionId, notifiableEvents) in eventsForSessions) { val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow() val imageLoader = imageLoaderHolder.get(client) @@ -207,7 +212,25 @@ class DefaultNotificationDrawerManager( } else { client.getUserProfile().getOrNull() ?: MatrixUser(sessionId) } - notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents, imageLoader) + if (isAppLocked) { + // When the app is locked, show a single fallback notification with event count + // instead of per-room notifications with message content/sender info. + clearAllMessagesEvents(sessionId) + val fallbackEvents = notifiableEvents.mapNotNull { it.toFallbackNotifiableEvent() } + notificationRenderer.render( + currentUser = currentUser, + useCompleteNotificationFormat = false, + eventsToProcess = fallbackEvents, + imageLoader = imageLoader, + ) + } else { + notificationRenderer.render( + currentUser = currentUser, + useCompleteNotificationFormat = useCompleteNotificationFormat, + eventsToProcess = notifiableEvents, + imageLoader = imageLoader, + ) + } } } } @@ -238,3 +261,29 @@ private fun AppNavigationState.shouldIgnoreEvent(event: NotifiableEvent): Boolea } } } + +/** + * Convert a [NotifiableEvent] into a [FallbackNotifiableEvent], stripping all content and sender info. + * Used when notification content should be hidden (app locked with PIN). + */ +private fun NotifiableEvent.toFallbackNotifiableEvent(): FallbackNotifiableEvent? { + val timestamp = when (this) { + is NotifiableMessageEvent -> timestamp + is InviteNotifiableEvent -> timestamp + is SimpleNotifiableEvent -> timestamp + is FallbackNotifiableEvent -> timestamp + is NotifiableRingingCallEvent -> return null + } + return FallbackNotifiableEvent( + sessionId = sessionId, + roomId = roomId, + eventId = eventId, + editedEventId = null, + description = null, + canBeReplaced = false, + isRedacted = false, + isUpdated = false, + timestamp = timestamp, + cause = null, + ) +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 150f4a9a2d..d814fcaeb2 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -67,7 +67,7 @@ class DefaultRoomGroupMessageCreator( val roomIsDm = !roomIsGroup return notificationCreator.createMessagesListNotification( notificationAccountParams = notificationAccountParams, - RoomEventGroupInfo( + roomInfo = RoomEventGroupInfo( sessionId = notificationAccountParams.user.userId, roomId = roomId, roomDisplayName = roomName, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index 04da6ef556..4e2685e63a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -13,6 +13,8 @@ import androidx.compose.ui.graphics.Color import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.features.lockscreen.api.LockScreenService +import io.element.android.features.lockscreen.test.FakeLockScreenService import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 @@ -510,6 +512,7 @@ fun TestScope.createDefaultNotificationDrawerManager( enterpriseService: EnterpriseService = FakeEnterpriseService(), sessionObserver: SessionObserver = FakeSessionObserver(), analyticsService: FakeAnalyticsService = FakeAnalyticsService(), + lockScreenService: LockScreenService = FakeLockScreenService(), ): DefaultNotificationDrawerManager { return DefaultNotificationDrawerManager( notificationDisplayer = notificationDisplayer, @@ -530,6 +533,7 @@ fun TestScope.createDefaultNotificationDrawerManager( matrixClientProvider = matrixClientProvider, imageLoaderHolder = FakeImageLoaderHolder(), activeNotificationsProvider = activeNotificationsProvider, + lockScreenService = lockScreenService, sessionObserver = sessionObserver, ) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt index 33071ef1ac..a0c233bc39 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationCreator.kt @@ -57,7 +57,17 @@ class FakeNotificationCreator( events: List, ): Notification { return createMessagesListNotificationResult( - listOf(notificationAccountParams, roomInfo, threadId, largeIcon, lastMessageTimestamp, tickerText, existingNotification, imageLoader, events) + listOf( + notificationAccountParams, + roomInfo, + threadId, + largeIcon, + lastMessageTimestamp, + tickerText, + existingNotification, + imageLoader, + events, + ) ) } diff --git a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt index 8ad2d62b33..fe9494a2c8 100644 --- a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt +++ b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/UserPushStoreDataStore.kt @@ -56,7 +56,6 @@ class UserPushStoreDataStore( private val currentPushKey = stringPreferencesKey("currentPushKey") private val notificationEnabled = booleanPreferencesKey("notificationEnabled") private val ignoreRegistrationError = booleanPreferencesKey("ignoreRegistrationError") - override suspend fun getPushProviderName(): String? { return store.data.first()[pushProviderName] }