You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Exploratory] Android - Notification banner is not displayed if app is open or minimized #93027
Version Number: 9.4.1-0 Reproducible in staging?: Yes Reproducible in production?: Yes If this was caught during regression testing, add the test name, ID and link from BrowserStack:#92497 Email or phone of affected tester (no customers):fischer9966+053122@gmail.com Issue reported by: Applause Internal Team Bug source: Pull Request QA execution Device used: Google Pixel 5 / Android 14 App Component: Notifications
Action Performed:
Open Expensify app for Android
Login with any account
Keep the app open on the chat list or minimize it
Trigger a push notification (e.g. send a chat message from another account to account from step 2)
Verify the notification banner appears and tapping it opens the correct chat.
Expected Result:
A notification banner appears if the app is open or minimized and the user is not in the chat where the message is being received.
Actual Result:
Notifications appears only in the notifications feed. Notification banner does not appears if the app is open or minimized and the user is not in the chat where the message is being received.
Android foreground push notification banners are suppressed by the logic in src/libs/Notification/PushNotification/shouldShowPushNotification.ts, which is called from the Airship foreground display predicate set in src/libs/Notification/PushNotification/ForegroundNotifications/index.android.ts (line 6):
shouldShowPushNotification delegates to Report.shouldShowReportActionNotification (src/libs/actions/Report/index.ts, line 4542). That function has two default-deny checks that fire when Onyx data is not yet populated:
getReportNotificationPreference (src/libs/ReportUtils.ts, line 1891–1896) returns HIDDEN when deprecatedCurrentUserAccountID is undefined or when the report's participants entry has no notificationPreference. The result is that most foreground banners are suppressed by default.
When allReports is not yet populated the predicate returns false and suppresses the banner.
Both gates are correct for local notifications (Pusher-based), where Onyx is fully populated before the check runs. But the Airship setForegroundDisplayPredicate fires as soon as the push arrives — before the Onyx collection is necessarily ready — so both gates produce false negatives.
For the minimized case: Airship's Android lifecycle observer keeps the process classified as "foregrounded" for roughly 700 ms after the user presses Home (to absorb brief transitions). Any push arriving in that window still calls the predicate. Visibility.isVisible() returns false because AppState.currentState !== 'active', so the same-chat guard on line 4572 does not fire, but the preference gate on line 4560 still returns false (stale data), suppressing the banner before Airship can hand it off to the native path.
Before this file was introduced, Android used the no-op ForegroundNotifications/index.ts, so Airship showed all foreground push banners without any filtering.
The iOS implementation (ForegroundNotifications/index.ios.ts) avoids this class of bug because in the background iOS never calls setForegroundPresentationOptionsCallback — native iOS always shows the banner — so the Onyx-data race only exists in the narrow foreground window where the navigation check is reliable.
What changes should we make
1. Replace shouldShowPushNotification.ts with a navigation-only check.
The only reliable signal available when the predicate fires (even before Onyx settles) is where the user is navigating. Mirror what the same-chat guard already does on line 4572 of shouldShowReportActionNotification — suppress only when the user is actively viewing the exact report that generated the push:
// src/libs/Notification/PushNotification/shouldShowPushNotification.tsimporttype{PushPayload}from'@ua/react-native-airship';importLogfrom'@libs/Log';importNavigationfrom'@libs/Navigation/Navigation';importVisibilityfrom'@libs/Visibility';importparsePushNotificationPayloadfrom'./parsePushNotificationPayload';exportdefaultfunctionshouldShowPushNotification(pushPayload: PushPayload): boolean{Log.info('[PushNotification] push notification received',false,{pushPayload});constdata=parsePushNotificationPayload(pushPayload.extras.payload);if(!data||data.reportID==null){returntrue;}// Suppress the banner only when the user is already viewing this exact report.consttopmostReportID=Navigation.getTopmostReportId();constshouldShow=String(data.reportID)!==topmostReportID||!Visibility.isVisible()||!Visibility.hasFocus();Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`);returnshouldShow;}
This removes the currentUserAccountID Onyx subscription and the shouldShowReportActionNotification dependency entirely. When Visibility.isVisible() is false (app minimized / AppState !== 'active'), the expression short-circuits to true, so banners that arrive during the Airship transition window are also unblocked.
2. Remove the now-unused currentUserAccountID Onyx subscription from the same file — the new body uses no Onyx reads.
3. Add a unit test in tests/unit/shouldShowPushNotificationTest.ts mocking Navigation.getTopmostReportId, Visibility.isVisible, and Visibility.hasFocus to verify:
Banner shows when the user is on a different report.
Banner is suppressed when the user is on the same report and the app is visible + focused.
Banner shows when the app is not visible (minimized state), even if the report ID matches.
No native code changes are required. The CustomNotificationProvider path and shouldShowReportActionNotification (still used by showReportActionNotification for Pusher-based local notifications) are untouched.
What alternatives did you explore
Guard only the Onyx-data paths inside shouldShowReportActionNotification for isRemote = true — e.g. return true when allReports is undefined. This is surgical but leaves the function with growing platform-specific branches. The notification-preference check is also semantically wrong here: the server already enforced preference when it decided to deliver the push; the foreground predicate's only job is "is the user already looking at this?"
Keep shouldShowPushNotification unchanged and instead always return true from ForegroundNotifications/index.android.ts, relying on the native CustomNotificationProvider to suppress — this would show banners even when the user is in the originating chat, which is the one case where suppression is clearly correct.
The proposed change is the smallest, most reliable fix: it removes all Onyx dependencies from the predicate and limits suppression to the one case (user is in the same chat, app in focus) where Airship foreground filtering is genuinely needed.
changed the title [-][Exploratory] 92988 - PR 92497 - Android - Notification banner is not displayed if app is open or minimized[/-][+][Exploratory] Android - Notification banner is not displayed if app is open or minimized[/+]on Jun 9, 2026
This issue has not been updated in over 15 days. eroding to Monthly issue.
P.S. Is everyone reading this sure this is really a near-term priority? Be brave: if you disagree, go ahead and close it out. If someone disagrees, they'll reopen it, and if they don't: one less thing to do!
Activity
applause-bot commentedon Jun 9, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/b46c612ab877cc3c1f3cb8b4c56ed0337844475af53050e3c14153400fe2ac3e
trasnake87 commentedon Jun 9, 2026
Proposal
What is the root cause
Android foreground push notification banners are suppressed by the logic in
src/libs/Notification/PushNotification/shouldShowPushNotification.ts, which is called from the Airship foreground display predicate set insrc/libs/Notification/PushNotification/ForegroundNotifications/index.android.ts(line 6):shouldShowPushNotificationdelegates toReport.shouldShowReportActionNotification(src/libs/actions/Report/index.ts, line 4542). That function has two default-deny checks that fire when Onyx data is not yet populated:Line 4559–4560 — notification preference gate:
getReportNotificationPreference(src/libs/ReportUtils.ts, line 1891–1896) returnsHIDDENwhendeprecatedCurrentUserAccountIDisundefinedor when the report'sparticipantsentry has nonotificationPreference. The result is that most foreground banners are suppressed by default.Line 4586–4590 — report existence gate:
When
allReportsis not yet populated the predicate returnsfalseand suppresses the banner.Both gates are correct for local notifications (Pusher-based), where Onyx is fully populated before the check runs. But the Airship
setForegroundDisplayPredicatefires as soon as the push arrives — before the Onyx collection is necessarily ready — so both gates produce false negatives.For the minimized case: Airship's Android lifecycle observer keeps the process classified as "foregrounded" for roughly 700 ms after the user presses Home (to absorb brief transitions). Any push arriving in that window still calls the predicate.
Visibility.isVisible()returnsfalsebecauseAppState.currentState !== 'active', so the same-chat guard on line 4572 does not fire, but the preference gate on line 4560 still returnsfalse(stale data), suppressing the banner before Airship can hand it off to the native path.Before this file was introduced, Android used the no-op
ForegroundNotifications/index.ts, so Airship showed all foreground push banners without any filtering.The iOS implementation (
ForegroundNotifications/index.ios.ts) avoids this class of bug because in the background iOS never callssetForegroundPresentationOptionsCallback— native iOS always shows the banner — so the Onyx-data race only exists in the narrow foreground window where the navigation check is reliable.What changes should we make
1. Replace
shouldShowPushNotification.tswith a navigation-only check.The only reliable signal available when the predicate fires (even before Onyx settles) is where the user is navigating. Mirror what the same-chat guard already does on line 4572 of
shouldShowReportActionNotification— suppress only when the user is actively viewing the exact report that generated the push:This removes the
currentUserAccountIDOnyx subscription and theshouldShowReportActionNotificationdependency entirely. WhenVisibility.isVisible()isfalse(app minimized /AppState !== 'active'), the expression short-circuits totrue, so banners that arrive during the Airship transition window are also unblocked.2. Remove the now-unused
currentUserAccountIDOnyx subscription from the same file — the new body uses no Onyx reads.3. Add a unit test in
tests/unit/shouldShowPushNotificationTest.tsmockingNavigation.getTopmostReportId,Visibility.isVisible, andVisibility.hasFocusto verify:No native code changes are required. The
CustomNotificationProviderpath andshouldShowReportActionNotification(still used byshowReportActionNotificationfor Pusher-based local notifications) are untouched.What alternatives did you explore
Guard only the Onyx-data paths inside
shouldShowReportActionNotificationforisRemote = true— e.g. returntruewhenallReportsisundefined. This is surgical but leaves the function with growing platform-specific branches. The notification-preference check is also semantically wrong here: the server already enforced preference when it decided to deliver the push; the foreground predicate's only job is "is the user already looking at this?"Keep
shouldShowPushNotificationunchanged and instead always returntruefromForegroundNotifications/index.android.ts, relying on the nativeCustomNotificationProviderto suppress — this would show banners even when the user is in the originating chat, which is the one case where suppression is clearly correct.The proposed change is the smallest, most reliable fix: it removes all Onyx dependencies from the predicate and limits suppression to the one case (user is in the same chat, app in focus) where Airship foreground filtering is genuinely needed.
[-][Exploratory] 92988 - PR 92497 - Android - Notification banner is not displayed if app is open or minimized[/-][+][Exploratory] Android - Notification banner is not displayed if app is open or minimized[/+]melvin-bot commentedon Jun 12, 2026
Whoops! This issue is 2 days overdue. Let's get this updated quick!
melvin-bot commentedon Jun 16, 2026
Still overdue 6 days?! Let's take care of this!
melvin-bot commentedon Jun 18, 2026
8 days overdue is a lot. Should this be a Weekly issue? If so, feel free to change it!
melvin-bot commentedon Jun 22, 2026
12 days overdue now... This issue's end is nigh!
melvin-bot commentedon Jun 25, 2026
This issue has not been updated in over 14 days. eroding to Weekly issue.
melvin-bot commentedon Jul 20, 2026
This issue has not been updated in over 15 days. eroding to Monthly issue.
P.S. Is everyone reading this sure this is really a near-term priority? Be brave: if you disagree, go ahead and close it out. If someone disagrees, they'll reopen it, and if they don't: one less thing to do!
melvin-bot commentedon Oct 2, 2026
@applause-bot[bot], this Monthly task hasn't been acted upon in 6 weeks; closing.
If you disagree, feel encouraged to reopen it -- but pick your least important issue to close instead.