Fix: make sure we ignore notifications for open rooms (#867)
* Make sure we ignore notifications for open rooms - Listen to process lifecycle changes in `AppForegroundStateService`. Use initializers to reliable create it. - Merge `AppNavigationState` with `AppForegroundState`. Renamed the previous `AppNavigationState` to `NavigationState`, created a new `AppNavigationState` which contains both the navigation state and the foreground state.
This commit is contained in:
parent
004b86b05d
commit
9247cd765a
26 changed files with 552 additions and 246 deletions
|
|
@ -32,9 +32,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
|||
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
||||
import io.element.android.libraries.push.api.store.PushDataStore
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreEventInRoom
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.NavigationState
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -65,7 +63,6 @@ class DefaultNotificationDrawerManager @Inject constructor(
|
|||
* Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events.
|
||||
*/
|
||||
private val notificationState by lazy { createInitialNotificationState() }
|
||||
private var currentAppNavigationState: AppNavigationState? = null
|
||||
private val firstThrottler = FirstThrottler(200)
|
||||
|
||||
// TODO EAx add a setting per user for this
|
||||
|
|
@ -74,26 +71,25 @@ class DefaultNotificationDrawerManager @Inject constructor(
|
|||
init {
|
||||
// Observe application state
|
||||
coroutineScope.launch {
|
||||
appNavigationStateService.appNavigationStateFlow
|
||||
.collect { onAppNavigationStateChange(it) }
|
||||
appNavigationStateService.appNavigationState
|
||||
.collect { onAppNavigationStateChange(it.navigationState) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAppNavigationStateChange(appNavigationState: AppNavigationState) {
|
||||
currentAppNavigationState = appNavigationState
|
||||
when (appNavigationState) {
|
||||
AppNavigationState.Root -> {}
|
||||
is AppNavigationState.Session -> {}
|
||||
is AppNavigationState.Space -> {}
|
||||
is AppNavigationState.Room -> {
|
||||
private fun onAppNavigationStateChange(navigationState: NavigationState) {
|
||||
when (navigationState) {
|
||||
NavigationState.Root -> {}
|
||||
is NavigationState.Session -> {}
|
||||
is NavigationState.Space -> {}
|
||||
is NavigationState.Room -> {
|
||||
// Cleanup notification for current room
|
||||
clearMessagesForRoom(appNavigationState.parentSpace.parentSession.sessionId, appNavigationState.roomId)
|
||||
clearMessagesForRoom(navigationState.parentSpace.parentSession.sessionId, navigationState.roomId)
|
||||
}
|
||||
is AppNavigationState.Thread -> {
|
||||
is NavigationState.Thread -> {
|
||||
onEnteringThread(
|
||||
appNavigationState.parentRoom.parentSpace.parentSession.sessionId,
|
||||
appNavigationState.parentRoom.roomId,
|
||||
appNavigationState.threadId
|
||||
navigationState.parentRoom.parentSpace.parentSession.sessionId,
|
||||
navigationState.parentRoom.roomId,
|
||||
navigationState.threadId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -225,7 +221,7 @@ class DefaultNotificationDrawerManager @Inject constructor(
|
|||
private suspend fun refreshNotificationDrawerBg() {
|
||||
Timber.v("refreshNotificationDrawerBg()")
|
||||
val eventsToRender = notificationState.updateQueuedEvents(this) { queuedEvents, renderedEvents ->
|
||||
notifiableEventProcessor.process(queuedEvents.rawEvents(), currentAppNavigationState, renderedEvents).also {
|
||||
notifiableEventProcessor.process(queuedEvents.rawEvents(), renderedEvents).also {
|
||||
queuedEvents.clearAndAdd(it.onlyKeptEvents())
|
||||
}
|
||||
}
|
||||
|
|
@ -275,8 +271,4 @@ class DefaultNotificationDrawerManager @Inject constructor(
|
|||
notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents)
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldIgnoreMessageEventInRoom(resolvedEvent: NotifiableMessageEvent): Boolean {
|
||||
return resolvedEvent.shouldIgnoreEventInRoom(currentAppNavigationState)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableEven
|
|||
import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreEventInRoom
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -31,18 +31,19 @@ private typealias ProcessedEvents = List<ProcessedEvent<NotifiableEvent>>
|
|||
|
||||
class NotifiableEventProcessor @Inject constructor(
|
||||
private val outdatedDetector: OutdatedEventDetector,
|
||||
private val appNavigationStateService: AppNavigationStateService,
|
||||
) {
|
||||
|
||||
fun process(
|
||||
queuedEvents: List<NotifiableEvent>,
|
||||
appNavigationState: AppNavigationState?,
|
||||
renderedEvents: ProcessedEvents,
|
||||
): ProcessedEvents {
|
||||
val appState = appNavigationStateService.appNavigationState.value
|
||||
val processedEvents = queuedEvents.map {
|
||||
val type = when (it) {
|
||||
is InviteNotifiableEvent -> ProcessedEvent.Type.KEEP
|
||||
is NotifiableMessageEvent -> when {
|
||||
it.shouldIgnoreEventInRoom(appNavigationState) -> {
|
||||
it.shouldIgnoreEventInRoom(appState) -> {
|
||||
ProcessedEvent.Type.REMOVE
|
||||
.also { Timber.d("notification message removed due to currently viewing the same room or thread") }
|
||||
}
|
||||
|
|
@ -55,7 +56,7 @@ class NotifiableEventProcessor @Inject constructor(
|
|||
else -> ProcessedEvent.Type.KEEP
|
||||
}
|
||||
is FallbackNotifiableEvent -> when {
|
||||
it.shouldIgnoreEventInRoom(appNavigationState) -> {
|
||||
it.shouldIgnoreEventInRoom(appState) -> {
|
||||
ProcessedEvent.Type.REMOVE
|
||||
.also { Timber.d("notification fallback removed due to currently viewing the same room or thread") }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
package io.element.android.libraries.push.impl.notifications.model
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
|
@ -69,18 +67,13 @@ data class NotifiableMessageEvent(
|
|||
/**
|
||||
* Used to check if a notification should be ignored based on the current app and navigation state.
|
||||
*/
|
||||
fun NotifiableEvent.shouldIgnoreEventInRoom(
|
||||
appNavigationState: AppNavigationState?
|
||||
): Boolean {
|
||||
val currentSessionId = appNavigationState?.currentSessionId() ?: return false
|
||||
return when (val currentRoomId = appNavigationState.currentRoomId()) {
|
||||
fun NotifiableEvent.shouldIgnoreEventInRoom(appNavigationState: AppNavigationState): Boolean {
|
||||
val currentSessionId = appNavigationState.navigationState.currentSessionId() ?: return false
|
||||
return when (val currentRoomId = appNavigationState.navigationState.currentRoomId()) {
|
||||
null -> false
|
||||
else -> isAppInForeground
|
||||
else -> appNavigationState.isInForeground
|
||||
&& sessionId == currentSessionId
|
||||
&& roomId == currentRoomId
|
||||
&& (this as? NotifiableMessageEvent)?.threadId == appNavigationState.currentThreadId()
|
||||
&& (this as? NotifiableMessageEvent)?.threadId == appNavigationState.navigationState.currentThreadId()
|
||||
}
|
||||
}
|
||||
|
||||
private val isAppInForeground: Boolean
|
||||
get() = ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue