Skip to content

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

Merged
torlando-tech merged 2 commits intomainfrom
fix/notification-suppression-when-backgrounded
Mar 11, 2026
Merged

fix: show notifications when app backgrounded on active conversation#649
torlando-tech merged 2 commits intomainfrom
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 message notifications were incorrectly suppressed while a conversation was open but the device screen was off or the app was backgrounded — because the ViewModel's active-conversation state persists across those lifecycle events. The fix wraps the suppression guard with a ProcessLifecycleOwner foreground check via an injected isAppInForeground lambda, ensuring notifications are only suppressed when the user can actually see the conversation.

  • Adds ProcessLifecycleOwner-backed isAppInForeground lambda to NotificationHelper and gates active-conversation suppression on it
  • Adds lifecycle-process dependency (already catalogued in libs.versions.toml)
  • Adds a new test for the backgrounded-but-active-conversation case, and updates the existing suppression test to explicitly set isAppInForeground = { true }
  • One pre-existing test (notifyMessageReceived not suppressed for different conversation) no longer exercises its intended code path because isAppInForeground is not mocked — the test passes via short-circuit rather than verifying the hash-comparison branch, leaving a latent regression gap

Confidence Score: 4/5

  • Safe to merge; the core logic fix is correct and the dependency addition is clean, with only a minor test coverage gap to address.
  • The ProcessLifecycleOwner.isAtLeast(STARTED) check is the standard Android idiom for detecting foreground state and correctly handles screen-off and app-backgrounded scenarios. The @VisibleForTesting injection approach is well-understood and the new test covers the key new path. Score is 4 rather than 5 solely because one existing test no longer exercises its stated intent due to the missing isAppInForeground mock.
  • NotificationHelperTest.kt — the notifyMessageReceived not suppressed for different conversation test needs isAppInForeground = { true } to properly cover the hash-comparison branch.

Important Files Changed

Filename Overview
app/src/main/java/com/lxmf/messenger/notifications/NotificationHelper.kt Adds isAppInForeground lambda (backed by ProcessLifecycleOwner) and gates the active-conversation suppression on it. Logic is correct; the @VisibleForTesting internal var injection pattern works but the guard is only applied to message notifications — no other notification type checks the foreground state, which is intentional here.
app/src/test/java/com/lxmf/messenger/notifications/NotificationHelperTest.kt New suppression tests correctly mock isAppInForeground, but the pre-existing notifyMessageReceived not suppressed for different conversation test does not — causing it to pass via short-circuit (backgrounded) rather than testing the hash-comparison branch when the app is in the foreground, leaving a regression gap.
app/build.gradle.kts Adds libs.lifecycle.process dependency, which is already defined in gradle/libs.versions.toml at the same lifecycle version as the other lifecycle artifacts. Clean, minimal change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[notifyMessageReceived called] --> B{Master toggle\nenabled?}
    B -- No --> Z[return — no notification]
    B -- Yes --> C{Message notifications\nenabled?}
    C -- No --> Z
    C -- Yes --> D{Has notification\npermission?}
    D -- No --> Z
    D -- Yes --> E{isAppInForeground?}
    E -- No --> G[Post notification]
    E -- Yes --> F{activeConversation\n== destinationHash?}
    F -- Yes --> Z
    F -- No --> G
Loading

Comments Outside Diff (1)

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

    Test no longer covers its stated intent

    The notifyMessageReceived not suppressed for different conversation test does not mock isAppInForeground. In a Robolectric test environment with no running Activity, ProcessLifecycleOwner.get().lifecycle.currentState will be below STARTED, so isAppInForeground() returns false. Because && short-circuits, the conversation hash comparison ("different_hash" == "abc123def456") is never evaluated.

    The test currently passes because the app is treated as "backgrounded" (short-circuit), not because the hashes differ. This means the test is no longer validating its stated intent: that a notification for a different conversation is not suppressed when the app is in the foreground.

    If someone accidentally removed or broke the hash-comparison branch of the condition, this test would still pass — the regression would go undetected.

    Fix: explicitly set isAppInForeground = { true } so the hash comparison is actually exercised:

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

@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Mar 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@torlando-tech torlando-tech merged commit 8250fdc into main Mar 11, 2026
14 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