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
After capturing a receipt photo, the transition to the "Confirm details" page adds noticeable latency. Even after improving the image capture pipeline itself, the rendering cost of the confirmation page may still be a bottleneck.
Goal
Profile the "Confirm details" page mount and render cycle to identify optimization opportunities. Reducing render time here would make the shutter-button-to-confirmation transition feel snappier.
Not sure if there is enough data for the manual shutter span, but here's the query filtered to native since that's the only place that it matters. Some of those look kind of wonky so some spans might not be getting canceled.
Daily update:
I was looking into this today so I can familiarise myself with the code and I am looking where we should add the spans and if we need only one span or more granular spans to be able to track all the parts of this flow.
I've been digging into the regression from the Slack thread, and it looks like the root cause is a telemetry bug rather than an Android slowdown. Since the recent Sentry SDK upgrade our spans are being cancelled and then dropped by the board's status:ok filter, which is why the sample collapsed so dramatically. 84% of shutter captures never reach the board. I'm working on a fix.
Looked into why Android is so much slower on this flow. Most of the gap comes from two phases:
ManualConfirmationMount: even on high-end Android it takes ~1.9x as long as iOS (77 vs 44ms p50). Other flows on the same devices are only ~1.3-1.5x slower on Android (ManualOpenReport 1.41x, ManualNavigateToReports 1.44x, ManualOpenCreateExpense 1.29x), so mount looks like an outlier. There's no Android-specific code in the confirmation screen, so my guess is it's how Android renders it: creating native views and measuring text for all the footer fields can be expensive on the new architecture. Mount is mostly pure CPU work though, so part of it may just be slower phones. I can't tell how much from Sentry alone.
ManualReceiptCapture: Android snapshots are smaller than iOS (~1190×1584 vs 1440×1920) but take ~1.8x longer (97 vs 55ms p50). So it's not the image size, it's probably how VisionCamera takes snapshots on Android (copies the preview off the GPU, then compresses to JPEG). The full-photo path (flash/landscape, ~3% of Android captures) takes ~1.6s on Android vs ~0.6s on iOS and drives the slow tail.
Next step is to profile the confirmation mount on a real Android device to confirm where the time goes, and check whether the VisionCamera v5 snapshot is any faster.
Checked the regression flagged in the latest Slack thread, and it looks like it’s not a real regression.
The 602 -> 791 jump probably comes from the telemetry fix Expensify/App#101877 . Before it, the board only picked up the fast captures. Since then, Android p95 mostly depends on how many flash shots land in each release, and mount p95 has stayed around 285-320ms.
Flash on Android more than doubled from September to October (~2.3% → ~5.2% of captures). At ~1.7s per shot, that’s enough to set Android p95. Could be seasonal, a UI change or a different mix of users, so I think it’s worth checking.
The one PR I’d keep an eye on is my own Expensify/App#100592 . It processes the full photo in the background, which on Android can overlap with the confirmation screen loading. Early 9.5.7 data is going the wrong way (mount p95 292 → 516ms), but it’s only ~180 samples. I’ll check again next week.
Other options to make Android faster:
Flash: the v5 upgrade (Expensify/App#102898) already sets qualityPrioritization, so I’ll check flash and Android p95 once it’s merged.
Snapshot: on v4, Android takes the snapshot on the UI thread. I’ll check whether v5 does the same, and if so, whether moving the JPEG encoding off the UI thread could be a proper fix
Closing the camera while the confirmation page mounts: no difference. Mount took the same time whether the camera stayed on or not, and with both takeSnapshot and takePhoto.
Moving the snapshot JPEG encoding off the UI thread (a patch on VisionCamera v4): no gain. Capture got ~15ms slower and mount ~11ms faster, so it evened out, even with a high-priority thread. Dropped it.
photoQualityBalance="speed" on Android: this one makes flash captures faster, but photos come out ~35% smaller and ~10-13% less sharp. I don’t think it’s worth trading image quality for speed here, so I’m not going to move forward with it.
Checked the major regression: the metric went from 463.72ms → 836.49ms (+80.4%).
It looks like the jump lines up with VisionCamera V5. On iOS, p95 was around 410ms until Oct 26, hit 680ms on Oct 29 and has been 820–900ms since. The likely root cause is that v5 has no snapshot on iOS anymore, so every scan takes a full photo instead. Before, ~98% of iOS scans used the snapshot, so capture got much slower: p95 went from ~210ms to ~630ms.
Checked a few possible fixes and opened 2 draft PRs:
Split receipt capture time into camera and file write: [NOQA] Split receipt capture time into camera and file write #105754, so we can see whether the slow part is the camera or writing the file. If the file write turns out to be a big part, we could follow Marc’s point about showing the photo from memory (skipping the file round trip) and write the file in the background, since we still need it on disk for offline support.
Activity
marcaaron commentedon Feb 26, 2026
Not sure if there is enough data for the manual shutter span, but here's the query filtered to native since that's the only place that it matters. Some of those look kind of wonky so some spans might not be getting canceled.
kubabutkiewicz commentedon Feb 26, 2026
Hi Jakub from Callstack, I would like to work on this issue.
kubabutkiewicz commentedon Feb 26, 2026
Daily update:
I was looking into this today so I can familiarise myself with the code and I am looking where we should add the spans and if we need only one span or more granular spans to be able to track all the parts of this flow.
186 remaining items
cristipaval commentedon Sep 17, 2026
Thanks @OlimpiaZurek! Subissues ^
OlimpiaZurek commentedon Sep 22, 2026
Update:
I've been digging into the regression from the Slack thread, and it looks like the root cause is a telemetry bug rather than an Android slowdown. Since the recent Sentry SDK upgrade our spans are being cancelled and then dropped by the board's
status:okfilter, which is why the sample collapsed so dramatically. 84% of shutter captures never reach the board. I'm working on a fix.OlimpiaZurek commentedon Sep 23, 2026
Update:
PR with the fix is open for review: #101877
cristipaval commentedon Sep 23, 2026
Thanks @OlimpiaZurek! I created an issue for it
OlimpiaZurek commentedon Oct 6, 2026
Update:
ManualConfirmationMount: even on high-end Android it takes ~1.9x as long as iOS (77 vs 44ms p50). Other flows on the same devices are only ~1.3-1.5x slower on Android (ManualOpenReport1.41x,ManualNavigateToReports1.44x,ManualOpenCreateExpense1.29x), so mount looks like an outlier. There's no Android-specific code in the confirmation screen, so my guess is it's how Android renders it: creating native views and measuring text for all the footer fields can be expensive on the new architecture. Mount is mostly pure CPU work though, so part of it may just be slower phones. I can't tell how much from Sentry alone.ManualReceiptCapture: Android snapshots are smaller than iOS (~1190×1584 vs 1440×1920) but take ~1.8x longer (97 vs 55ms p50). So it's not the image size, it's probably how VisionCamera takes snapshots on Android (copies the preview off the GPU, then compresses to JPEG). The full-photo path (flash/landscape, ~3% of Android captures) takes ~1.6s on Android vs ~0.6s on iOS and drives the slow tail.Julesssss commentedon Oct 8, 2026
Reworked quality PR merged, we'll keep an eye on both timing and image quality.
OlimpiaZurek commentedon Oct 14, 2026
Update:
Checked the regression flagged in the latest Slack thread, and it looks like it’s not a real regression.
Other options to make Android faster:
qualityPrioritization, so I’ll check flash and Android p95 once it’s merged.OlimpiaZurek commentedon Oct 15, 2026
Update:
Tried a few Android-specific ideas:
takeSnapshotandtakePhoto.photoQualityBalance="speed"on Android: this one makes flash captures faster, but photos come out ~35% smaller and ~10-13% less sharp. I don’t think it’s worth trading image quality for speed here, so I’m not going to move forward with it.cristipaval commentedon Oct 16, 2026
Thanks @OlimpiaZurek, I agree with dropping these
OlimpiaZurek commentedon Nov 3, 2026
Update:
Checked a few possible fixes and opened 2 draft PRs:
mountiny commentedon Nov 3, 2026
Thanks! created issues for the draft prs