Skip to content

fix: show notifications when app backgrounded on active conversation#648

Merged
torlando-tech merged 2 commits intorelease/v0.9.xfrom
fix/notification-suppression-when-backgrounded
Mar 11, 2026
Merged

fix: show notifications when app backgrounded on active conversation#648
torlando-tech merged 2 commits intorelease/v0.9.xfrom
fix/notification-suppression-when-backgrounded

Conversation

@torlando-tech
Copy link
Copy Markdown
Owner

Summary

  • Notifications for the active conversation were suppressed even when the screen was off or the app was in the background, because the ViewModel (and its active conversation state) survives those lifecycle events
  • Added a ProcessLifecycleOwner foreground check so notifications are only suppressed when the app is actually visible
  • Added lifecycle-process dependency to the app module

Test plan

  • Open a conversation, lock screen, send a message from the other device — notification now appears
  • Open a conversation, switch to another app, send a message — notification now appears
  • Open a conversation while app is in foreground, send a message — notification still suppressed (existing behavior preserved)

🤖 Generated with Claude Code

Notifications were suppressed for the active conversation even when the
screen was off or the app was in the background, because the ViewModel
(and its active conversation state) survives those lifecycle events.
Now checks ProcessLifecycleOwner to confirm the app is actually in the
foreground before suppressing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR fixes a bug where notifications for the active conversation were incorrectly suppressed even when the screen was locked or the app was backgrounded, by adding a ProcessLifecycleOwner foreground check that gates the existing suppression logic. The lifecycle-process dependency is added, and an @VisibleForTesting-annotated isAppInForeground lambda is introduced on NotificationHelper to keep the new behavior unit-testable via Robolectric.

Key changes:

  • NotificationHelper: Suppression now requires both isAppInForeground() and the active-conversation hash match (line 166), correctly sending notifications when the screen is off or the app is in the background.
  • app/build.gradle.kts: Adds libs.lifecycle.process to provide ProcessLifecycleOwner.
  • NotificationHelperTest: Updated the existing suppression test and added a new test for the backgrounded case; however, two pre-existing "not suppressed" tests do not override isAppInForeground, causing them to pass trivially in Robolectric (where the process lifecycle never reaches STARTED) rather than validating the intended conversation-hash logic.

Confidence Score: 3/5

  • Core logic change is sound, but a pre-existing threshold concern and two under-specified tests leave minor gaps before merging.
  • The production fix is minimal and well-scoped. The STARTED threshold (vs RESUMED) concern noted in a previous thread remains unresolved, leaving a brief window where notifications can still be suppressed after the screen locks. Additionally, two pre-existing Robolectric tests now pass for the wrong reason, reducing confidence in test coverage of the foreground-but-wrong-conversation path.
  • NotificationHelperTest.kt — pre-existing suppression tests need isAppInForeground = { true } to be meaningful after this refactor.

Important Files Changed

Filename Overview
app/build.gradle.kts Adds lifecycle-process dependency needed for ProcessLifecycleOwner; straightforward and correct.
app/src/main/java/com/lxmf/messenger/notifications/NotificationHelper.kt Introduces isAppInForeground lambda (injected for testability) and adds a foreground guard to the notification suppression check; uses proper top-level imports. One open concern (STARTED threshold) was flagged in a previous review thread.
app/src/test/java/com/lxmf/messenger/notifications/NotificationHelperTest.kt Updated suppression test and added a new backgrounded test; two pre-existing "not suppressed" tests silently pass in Robolectric for the wrong reason — they should also set isAppInForeground = { true } to validate the conversation-hash branch.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[notifyMessageReceived called] --> B{Master toggle on?}
    B -- No --> Z[return — no notification]
    B -- Yes --> C{Message notifications enabled?}
    C -- No --> Z
    C -- Yes --> D{Has notification permission?}
    D -- No --> Z
    D -- Yes --> E{isAppInForeground\nAND\nactiveConversation == hash?}
    E -- Yes → suppress --> Z
    E -- No → allow --> F[Build & post notification]
Loading

Comments Outside Diff (1)

  1. app/src/test/java/com/lxmf/messenger/notifications/NotificationHelperTest.kt, line 224-262 (link)

    Pre-existing "not suppressed" tests pass trivially in Robolectric

    Both notifyMessageReceived not suppressed for different conversation (line 224) and notifyMessageReceived not suppressed when no conversation active (line 244) do not override isAppInForeground. In a Robolectric JVM environment, ProcessLifecycleOwner never reaches STARTED, so the default lambda returns false. That makes the suppression guard:

    if (isAppInForeground() && activeConversation == hash) return
    // becomes: if (false && ...) return  →  never suppresses

    These tests pass because the app is never considered "in foreground", not because of the conversation-hash mismatch they claim to test. If the foreground check were ever reordered or the default changed, these tests would give no signal. Both should set isAppInForeground = { true } to properly exercise the foreground-but-wrong-conversation path:

    // notifyMessageReceived not suppressed for different conversation
    activeConversationFlow.value = "different_hash"
    notificationHelper.isAppInForeground = { true }   // ← add this
    
    // notifyMessageReceived not suppressed when no conversation active
    activeConversationFlow.value = null
    notificationHelper.isAppInForeground = { true }   // ← add this

Last reviewed commit: 7c4713c

Extract foreground check into @VisibleForTesting property so tests can
control it. Update existing suppression test to simulate foreground state
and add new test verifying notifications fire when app is backgrounded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@torlando-tech
Copy link
Copy Markdown
Owner Author

@greptileai

@torlando-tech torlando-tech merged commit 3a35c17 into release/v0.9.x Mar 11, 2026
13 checks passed
@torlando-tech torlando-tech deleted the fix/notification-suppression-when-backgrounded branch March 11, 2026 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant