paint-out-status-bar
Paints the device’s status bar out of a captured frame, so a device capture can be committed.
A framebuffer carries the status bar, and a status bar carries a wall clock, a battery that charges while the emulator runs and a radio that comes and goes. So re-running a capture suite on an unchanged tree rewrites its PNGs with content nobody can commit, and the churn is invisible until someone diffs two runs. device-screenshot’s header has always said a device capture is not byte-reproducible; this is what makes one.
Pinning the bar was tried first, and is NOT sufficient — that finding is the reason this helper exists rather than a withSystemUiDemoMode one. SystemUI demo mode (sysui_demo_allowed, plus one com.android.systemui.demo broadcast per area: clock at a fixed hhmm, battery full and unplugged, radios hidden, notification icons off) held the clock at 12:00 and two runs still disagreed on 1823 full-contrast pixels, because the bar’s leading group is laid out at a different offset between emulator boots — intermittently, roughly one boot in four. The Tuner’s icon_blacklist does not help either: demo mode draws its own icon layer and ignores it. Measured on a real AVD across five paired capture runs for obsidian-link-picker’s store frames, which is where this recipe was worked out before it moved here.
Removing the band rather than pinning it also retires every device setting that pinning needed, and nothing in a status bar is evidence about a plugin. A listing frame without one is what a listing frame normally looks like.
The band’s height is the caller’s to measure, and there is deliberately no default. A wrong height does not fail loudly by itself — it paints over the product. What stands between the two is resolveStatusBarBand: the row just under the bar’s own content must be one flat color all the way across, which is what an empty band looks like and is also where the fill color is sampled from. A taller bar, or an Obsidian that draws higher, fails the capture instead.
Compositing over a captured frame is not a new liberty: labelScreenshot already draws the caption band across the bottom of the same image.
Interfaces
Section titled “Interfaces”| Interface | Description |
|---|---|
| BuildStatusBarBandFailureMessageParams | Parameters for buildStatusBarBandFailureMessage. |
| PaintOutStatusBarOptions | Options for paintOutStatusBar. |
| ResolveStatusBarBandParams | Parameters for resolveStatusBarBand. |
| StatusBarBandColor | A color read out of a frame, channel by channel. This package’s own type rather than sharp’s, because it is part of the public surface and sharp is an OPTIONAL peer: a consumer that never installs it still names this. Structurally what sharp’s background takes, which is how the fill block is created from one with no conversion in between. |
| StatusBarBandVerdict | What the row under the status bar’s content turned out to hold. Verdict-as-data, like the compatibility and teardown checks: the reading and the throwing are separate, so a caller can ask what a frame looks like without having to catch an error to find out. |
Functions
Section titled “Functions”| Function | Description |
|---|---|
| buildStatusBarBandFailureMessage | Builds the message a band that is not clear is refused with. Separate from the throwing so it can be asserted on directly: what makes this failure actionable is the measurement, not the wording around it. |
| paintOutStatusBar | Fills the status-bar band with the background behind it, so the frame carries no device chrome. The image keeps its dimensions exactly: the band is composited OVER the frame, never cropped off it, because a store listing expects a specific size and a cropped frame is a different picture. |
| resolveStatusBarBand | Reads the row under the status bar’s content and says whether the band is clear of the app. The band’s height is a number a caller measured once, and a measured number can go stale — a taller status bar, or an Obsidian that draws higher, would have the capture quietly paint over the product. So the row just below the bar’s own content is required to be one flat color all the way across: that is what an empty band looks like, and it is what the fill color is read from. |
Variables
Section titled “Variables”| Variable | Description |
|---|---|
| DEFAULT_STATUS_BAR_EDGE_MARGIN_IN_PIXELS | How far in from each edge the clear-of-chrome check reads, in pixels. Exported because a caller tightening or loosening it wants to say so relative to this rather than in the abstract. |
| DEFAULT_STATUS_BAR_SAMPLE_INSET_IN_PIXELS | How far above the band’s bottom edge the sample row sits, in pixels. Four rows up: far enough to clear the bar’s own content, close enough to still be inside the band on every device this has been measured on. |