Repository navigation
fix(mobile): let the Android app draw its own push notifications - #2465
Conversation
An Expo push with a title reaches Android as an FCM notification message, which the Firebase SDK draws while the app is in the background, so the conversation style, avatars and approval buttons never applied. The app now registers with render "app", and the Host sends that device data-only pushes with the shown title and body in the data. A handling delegate fills them in so expo-notifications presents the push through the conversation presentation. Older builds keep the system-drawn push. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrtPca5pD72Gdx4CFkQdNi
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrtPca5pD72Gdx4CFkQdNi
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 27 minutes. View limit details
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. Comment |
expo-notifications parses data.body as the JSON payload, so keep the shown text out of that key. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JrtPca5pD72Gdx4CFkQdNi
|
Android phone check: data-only pushes sent through the Expo push service are not displayed (Xiaomi, Android 16 / HyperOS 3, store build from main 5c351d0, battery saver set to unrestricted and autostart on, pushes sent from a script directly to the Expo push API) Evidence: 2397-android-push/phone (commit 77956f8):
Cause: expo-notifications 58.1.5 has a breaking change ("make data-only notifications consistent with iOS"). It treats a remote message without a notification part as silent. It only displays one when the top-level FCM Possible fixes:
Whatever the approach, HyperOS still drops pushes while the app is frozen. Generated by Claude Code |
|
This is a reply to the phone check. That test ran a build from main 5c351d0, which does not include this PR's native change, so "data-only is treated as silent" is the expected result there. It is also the root cause this PR fixes.
We'll hold off merging until an APK built from this branch passes on the phone. If it still doesn't show, we'll switch to registering a background task that posts a local notification. Generated by Claude Code |
…ata-only probes) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Android phone result for this PR (b14c3d3): the foreground passes, but the background still fails (Xiaomi, Android 16 / HyperOS 3, store build plus isolated QA Host from b14c3d3, device re-registered, real Host pushes) Evidence: 2465-android-push/phone (commit 7ba8808):
Cause: with Suggested fixes:
Generated by Claude Code |
|
Android phone result on main 2d0bf6e (#2465 + #2483 + #2485): PASS, with one new issue (Xiaomi, Android 16 / HyperOS 3, fresh isolated QA Host and store build, device registered with Evidence: 2465-android-push/phone-main (commit 5784528): Background and foreground were each tested with a Mira DM reply, an approval (Nova in the background, Echo in the foreground), a secret-input request and a group channel reply:
New issue: an unread DM notification is removed by the badge push. In the background run, the Mira DM reply (posted 19:26:58) and the secret-input request were both removed in the same second, 19:27:46, the moment the Host sent a Separately, on b485d61 one Mira DM reply was accepted by Expo (ticket ok) but never appeared. A resend worked, and the cause is unknown; it may be the same removal. Generated by Claude Code |
|
Android phone result on main 37971eb (#2491): an unread DM now stays (PASS), but handled request notifications are not removed (FAIL) (Xiaomi, Android 16 / HyperOS 3, fresh isolated QA Host and store build, battery saver unrestricted and autostart on, app in the background, real Host pushes) Evidence: 2465-android-push/phone-37971eba (commit cf3e655): Steps: (1) Mira DM reply, left unread. (2) Echo secret-input request, submitted with fake data. (3) Echo secret_cli approval, denied via the CLI. Echo replied QA-SECRET-REFUSED, and the Host sent a badge push.
Likely cause (inferred; the release build can't be debugged): Generated by Claude Code |
|
Android phone check: all cases pass (Xiaomi, Android 16 / HyperOS 3, store build, Host on the same commit; the final round used main 77ddca9). This covers #2465, #2483, #2485, #2491 and #2494.
Details: tap and action result. Known behavior, not addressed in this series: opening a chat clears all of that chat's notifications, including a still-pending approval. On HyperOS, the action buttons only show after the notification is expanded. Generated by Claude Code |
Requested by Vain · project thread
Why
The #2397 Android phone check (evidence, root cause) found every notification posted by the FCM SDK (tag
FCM-Notification, BigTextStyle), with no conversation style, no avatar and no approval buttons. An Expo push with a top-leveltitlereaches Android as an FCM notification message. While the app is in the background, the Firebase SDK draws that message itself, so expo-notifications and ourConversationNotificationsServicenever see it.Change outline
Host (
packages/core/src/push)registeracceptsrender: "app", stored in a newpush_devices.rendercolumn (migration 105).render = 'app', a Bot push has no top-leveltitle/body, which makes it data-only. The shown text goes into the data astitleandmessage, pluscategoryId. The text is not put indata.body, because expo-notifications parses that key as JSON.priority: 'high',channelIdandcategoryIdstay on the message. iOS devices and older Android builds get exactly what they got before.App
push-sessionregisters withrender: 'app'on Android.ConversationNotificationsServicealso overrides the handling delegate. Expo presents a background data-only push only when it has a title or text, soConversationHandlingDelegatefirst rebuilds the content withtitle/texttaken from the data. It keeps sound, vibration, category, badge and the same trigger/channel, then hands the content to the stockExpoHandlingDelegate. Presentation then goes throughConversationPresentationDelegate(MessagingStyle, avatars, shortcut) and expo's category actions.Verification
pnpm typecheckandoxfmt --checkpass. Push delivery tests pass (18), including a new test of the data-only Android payload. Mobile push tests pass (13), including a real-Host gateway test where an Android session registers and receives a data-only push.:conversation-notifications:compileDebugKotlin).Special things to note
channelId/categoryIdas top-level FCM data keys.categoryIdis also sent in our data as a fallback. If the channel doesn't map, the notification uses expo's default channel. The phone check will show which happens.🤖 Generated with Claude Code
https://claude.ai/code/session_01JrtPca5pD72Gdx4CFkQdNi