feat(android): promote running sessions to an ongoing notification - #423
Open
RonenMars wants to merge 3 commits into
Open
feat(android): promote running sessions to an ongoing notification#423RonenMars wants to merge 3 commits into
RonenMars wants to merge 3 commits into
Conversation
RonenMars
force-pushed
the
fix/cold-start-deep-links
branch
from
July 25, 2026 00:14
344bae9 to
08cd2f4
Compare
RonenMars
force-pushed
the
feat/live-activity-android
branch
from
July 25, 2026 00:14
72d0f24 to
8fe8312
Compare
RonenMars
force-pushed
the
fix/cold-start-deep-links
branch
from
July 25, 2026 21:22
08cd2f4 to
27a4db5
Compare
RonenMars
force-pushed
the
feat/live-activity-android
branch
from
July 25, 2026 21:22
8fe8312 to
47e34ba
Compare
Surfaces a running session on Android as an ongoing notification, resolved through Metro's platform extension so the call site in _layout stays platform-agnostic. decideActions is reused unchanged, so the cap of three and the least-recently-updated eviction behave identically to iOS. This is a plain ongoing notification, not the Android 16 promoted status-bar chip the runbook aimed at. expo-notifications exposes `sticky`, which maps to setOngoing, but neither setRequestPromotedOngoing nor setUsesChronometer, so there is no chip to request and no chronometer to drive. Elapsed time is therefore omitted rather than rendered: without a chronometer it could only be a snapshot that silently goes stale, and a wrong timer is worse than none. Closing that gap needs those builder calls exposed upstream or a small native module — noted in docs/expo-web-support.md rather than papered over. POST_PROMOTED_NOTIFICATIONS is deliberately not declared, since nothing can request promotion yet and an unused permission is just noise in the manifest. Posting with an existing identifier replaces the notification in place, so start and update are the same call and an update cannot stack a duplicate. Notifications are tagged `liveSession` in their data payload, because adoption after a restart has to clear our stale surfaces without dismissing push notifications, which also carry a sessionId. The Platform.OS === 'ios' guards in the iOS module are dropped: with .android.ts and .web.ts siblings present, that file is only ever the iOS resolution and the checks were dead.
RonenMars
force-pushed
the
fix/cold-start-deep-links
branch
from
July 25, 2026 21:43
27a4db5 to
b7e37b9
Compare
RonenMars
force-pushed
the
feat/live-activity-android
branch
from
July 25, 2026 21:43
47e34ba to
7f7d726
Compare
getRawLines() only dropped blank rows, so Claude Code's status-bar box borders leaked into the raw terminal view as literal characters when parse confidence fell to low.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #422. Final PR of the five — Phase 1b (APNs + renewal) is out of scope.
Surfaces a running session on Android as an ongoing notification, resolved through Metro's platform extension so the call site in
_layoutstays platform-agnostic.decideActionsis reused unchanged, so the cap of three and least-recently-updated eviction behave identically to iOS.This is not the promoted status-bar chip, and cannot be yet
The plan targeted Android 16's promoted ongoing notification via
setRequestPromotedOngoing+setUsesChronometer.expo-notificationsexposes neither. It offerssticky, which maps tosetOngoing, and that is the whole surface — there is no chip to request and no chronometer to drive.So this lands as the plain ongoing notification the runbook names as the acceptable v1 floor.
Elapsed time is omitted rather than rendered: without a chronometer it could only be a snapshot that silently goes stale, and a wrong timer is worse than no timer.
Closing the gap needs those two builder calls exposed upstream or a small native module.
Recorded in
docs/expo-web-support.mdrather than papered over.POST_PROMOTED_NOTIFICATIONSis deliberately not declared — nothing can request promotion, and an unused permission is just noise in the manifest.POST_NOTIFICATIONSis added, since it was previously declared nowhere.Implementation notes
Posting with an existing identifier replaces the notification in place, so start and update are the same call and an update cannot stack a duplicate.
Notifications are tagged
liveSession: truein their data payload.Adoption after a restart has to clear our own stale surfaces without dismissing push notifications, which also carry a
sessionId— filtering on that alone would have cleared the wrong things.The channel is created at
IMPORTANCE_DEFAULT; anything lower forfeits promotion on API 36+ and can be collapsed out of the shade entirely.The
Platform.OS === 'ios'guards in the iOS module are dropped: with.android.tsand.web.tssiblings present, that file is only ever the iOS resolution and the checks were dead code.Verification
6 new unit tests: payload shape,
waiting_inputvsrunning, in-place replacement, terminal dismissal, shared cap eviction, and that adoption spares push notifications.npm run test:unit930/930 green; lint 0 errors; typecheck unchanged from baseline.Device verification on Android 13–15 and 16+ hardware is still pending, including the aggressive-battery OEM check.
CI is red on this branch, and it was red before this work
Type check,i18n, andIntegration testsfail here. None of those failures come from this stack.Measured on a clean worktree at
integration-merge-354-355-376(f2242bc4) with no changes applied:makeSearchStylesinapp/conversation/[id].tsx:67, plus two i18n key-signature errors incomponents/onboarding/steps/ConnectStep.tsx:196,197.__tests__/integration/conversation-*.CI on this branch reports the identical
Test Suites: 4 failed, 36 passed/Tests: 34 failed, 211 passed, andtscreports the same 3 errors and no others.Independently confirmed by the repo owner against their own clean worktree.
These are deliberately left alone — fixing them is unrelated to Live Activities and belongs in its own PR.