Skip to content

fix(Android): app shows notifications in foreground state#7012

Draft
Rohit3523 wants to merge 1 commit intodevelopfrom
notification-fix
Draft

fix(Android): app shows notifications in foreground state#7012
Rohit3523 wants to merge 1 commit intodevelopfrom
notification-fix

Conversation

@Rohit3523
Copy link
Contributor

@Rohit3523 Rohit3523 commented Feb 26, 2026

Proposed changes

When we are using the app and someone sends a message in any channel, the app shows a blank notification without content. This PR adds a check at the native level to prevent triggering notifications when the app is in the foreground

Issue(s)

https://rocketchat.atlassian.net/browse/CORE-1856
Closes: #6999

How to test or reproduce

Screenshots

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

Summary by CodeRabbit

  • Bug Fixes
    • Improved notification behavior by implementing intelligent app state detection. Native system notifications now display only when the app is in the background, while in-app notifications appear when the app is active. This reduces duplicate notifications and provides a more seamless notification experience.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java (2)

249-257: ⚠️ Potential issue | 🟠 Major

Avoid queueing notifications that are intentionally suppressed in foreground.

Lines 253-257 still append to notificationMessages even when Line 294 later suppresses display. This can retain stale data and inflate grouped notifications once app goes background.

Suggested fix
-            notificationMessages.get(notId).add(bundle);
-            if (ENABLE_VERBOSE_LOGS) {
-                Log.d(TAG, "[After add] notificationMessages[" + notId + "].size=" + notificationMessages.get(notId).size());
-            }
-            postNotification(Integer.parseInt(notId));
+            if (isAppInForeground()) {
+                if (ENABLE_VERBOSE_LOGS) {
+                    Log.d(TAG, "App in foreground, skipping native queue/display for notId=" + notId);
+                }
+                return;
+            }
+            notificationMessages.get(notId).add(bundle);
+            if (ENABLE_VERBOSE_LOGS) {
+                Log.d(TAG, "[After add] notificationMessages[" + notId + "].size=" + notificationMessages.get(notId).size());
+            }
+            postNotification(Integer.parseInt(notId));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java`
around lines 249 - 257, The code is adding bundle entries to
notificationMessages (keyed by notId) even for notifications that are later
suppressed in foreground; update the block around
notificationMessages.get(notId).add(bundle) to first check the suppression flag
on the bundle (e.g., bundle.getBoolean("notificationLoaded", false) or the
foreground-suppression flag used later) and only add and call
postNotification(Integer.parseInt(notId)) when the bundle is not suppressed;
keep the verbose Log.d(TAG, ...) calls but move them inside the guarded branch
so suppressed notifications are neither queued nor cause postNotification to
run.

51-91: ⚠️ Potential issue | 🔴 Critical

Make isAppInForeground thread-safe.

The field at line 51 is written from MainActivity's lifecycle callbacks (main thread) and read during Firebase message processing (background thread). Without synchronization, memory visibility is not guaranteed across threads.

Suggested fix
-    private static boolean isAppInForeground = false;
+    private static volatile boolean isAppInForeground = false;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java`
around lines 51 - 91, The static field isAppInForeground is accessed from
multiple threads without synchronization; make it thread-safe by replacing the
plain boolean with a thread-safe construct (e.g.,
java.util.concurrent.atomic.AtomicBoolean) and update all usages: initialize a
private static final AtomicBoolean isAppInForeground = new AtomicBoolean(false),
change setAppInForeground(...) to call isAppInForeground.set(inForeground) and
change isAppInForeground() to return isAppInForeground.get(); ensure any other
direct reads/writes to the old boolean are updated to use the AtomicBoolean
methods (references: isAppInForeground field, setAppInForeground method,
isAppInForeground() method).
🧹 Nitpick comments (1)
android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt (1)

38-48: Consider process-level lifecycle tracking for more robust foreground state management.

Activity-level onResume/onPause can briefly mark the app as backgrounded during transitions to other activities (e.g., ShareActivity). This could allow native notifications to display during activity transitions. Use ProcessLifecycleOwner from androidx.lifecycle for process-level tracking instead—this survives activity transitions and only changes when the entire app moves to background.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt` around
lines 38 - 48, The current foreground/background tracking in MainActivity via
onResume/onPause (which calls CustomPushNotification.setAppInForeground) can
misfire during activity-to-activity transitions; replace or supplement this with
a process-level lifecycle observer using
androidx.lifecycle.ProcessLifecycleOwner: create a LifecycleObserver (or
implement DefaultLifecycleObserver) that calls
CustomPushNotification.setAppInForeground(true/false) on onStart/onStop
(process-level), register it in the Application class (e.g., in onCreate) so
state changes reflect the whole app lifecycle rather than MainActivity
transitions, and remove or keep MainActivity.onResume/onPause only if you need
activity-specific behaviour.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java`:
- Around line 249-257: The code is adding bundle entries to notificationMessages
(keyed by notId) even for notifications that are later suppressed in foreground;
update the block around notificationMessages.get(notId).add(bundle) to first
check the suppression flag on the bundle (e.g.,
bundle.getBoolean("notificationLoaded", false) or the foreground-suppression
flag used later) and only add and call postNotification(Integer.parseInt(notId))
when the bundle is not suppressed; keep the verbose Log.d(TAG, ...) calls but
move them inside the guarded branch so suppressed notifications are neither
queued nor cause postNotification to run.
- Around line 51-91: The static field isAppInForeground is accessed from
multiple threads without synchronization; make it thread-safe by replacing the
plain boolean with a thread-safe construct (e.g.,
java.util.concurrent.atomic.AtomicBoolean) and update all usages: initialize a
private static final AtomicBoolean isAppInForeground = new AtomicBoolean(false),
change setAppInForeground(...) to call isAppInForeground.set(inForeground) and
change isAppInForeground() to return isAppInForeground.get(); ensure any other
direct reads/writes to the old boolean are updated to use the AtomicBoolean
methods (references: isAppInForeground field, setAppInForeground method,
isAppInForeground() method).

---

Nitpick comments:
In `@android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt`:
- Around line 38-48: The current foreground/background tracking in MainActivity
via onResume/onPause (which calls CustomPushNotification.setAppInForeground) can
misfire during activity-to-activity transitions; replace or supplement this with
a process-level lifecycle observer using
androidx.lifecycle.ProcessLifecycleOwner: create a LifecycleObserver (or
implement DefaultLifecycleObserver) that calls
CustomPushNotification.setAppInForeground(true/false) on onStart/onStop
(process-level), register it in the Application class (e.g., in onCreate) so
state changes reflect the whole app lifecycle rather than MainActivity
transitions, and remove or keep MainActivity.onResume/onPause only if you need
activity-specific behaviour.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 566e100 and 9de14d3.

📒 Files selected for processing (2)
  • android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt
  • android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-05T13:55:06.688Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6930
File: package.json:101-101
Timestamp: 2026-02-05T13:55:06.688Z
Learning: The RocketChat/Rocket.Chat.ReactNative repository uses a fork of react-native-image-crop-picker (RocketChat/react-native-image-crop-picker) with custom Android edge-to-edge fixes, not the upstream ivpusic/react-native-image-crop-picker package. Dependencies should reference commit pins from the RocketChat fork.

Applied to files:

  • android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt
🧬 Code graph analysis (1)
android/app/src/main/java/chat/rocket/reactnative/notification/CustomPushNotification.java (1)
app/reducers/app.ts (1)
  • app (29-77)

@Rohit3523 Rohit3523 requested a deployment to upload_experimental_android February 26, 2026 20:10 — with GitHub Actions Waiting
@github-actions
Copy link

Android Build Available

Rocket.Chat Experimental 4.70.0.108316

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNTIczwBTkj4_LK_6nz0RjLp21gpXPIAP_oq_5GB1G4_7EBNxXXtqSU3uqXL7ZdSZmgwHLtgz1S1yz0zm52M

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Notifications sent for the channel I'm currently in

1 participant