Skip to content

Enable edge-to-edge display cutout in fullscreen mode - #7618

Open
TalviFox wants to merge 7 commits into
home-assistant:mainfrom
TalviFox:fix/hardcoded-safe-area
Open

TalviFox wants to merge 7 commits into
home-assistant:mainfrom
TalviFox:fix/hardcoded-safe-area

Conversation

@TalviFox

Copy link
Copy Markdown

Summary

Fixes #7554

This PR allows the WebView canvas to extend behind the camera hole punch in fullscreen mode, and removes the old status bar overlay guardrail in FrontendScreen.kt.

What changed:

  • Configured layoutInDisplayCutoutMode = SHORT_EDGES in LaunchActivity when fullscreen is toggled. Without this, Android defaults to letterboxing the window when system bars are hidden.
  • Removed statusBarColor.Overlay in FrontendScreen. This was originally added as a defensive measure for frontend#29125 and drawer contrast, but modern Lovelace already handles safe area padding cleanly via CSS env(safe-area-inset-top).
  • Passed the real top inset (pxToDp(...)) to onSafeAreaInsetsChanged instead of hardcoding 0f.

Impact:

  • Standard mode: Unchanged. Headers, system bars, and drawer contrast behave normally.
  • Fullscreen mode: Drops the artificial black bar on hole-punch displays. CSS safe areas dynamically protect cards and alert banners from colliding with the physical camera.
  • Verified on hardware (Pixel 9 Pro XL).

Checklist

  • New or updated tests have been added to cover the changes following the testing guidelines.
  • The code follows the project's code style and best_practices.
  • The changes have been thoroughly tested, and edge cases have been considered.
  • Changes are backward compatible whenever feasible. Any breaking changes are documented in the changelog for users and/or in the code for developers depending on the relevance.
  • I have read the Open Home Foundation AI Policy.

Select exactly one option that describes AI usage in this contribution:

  • I have not used AI for this contribution.
  • AI assistance was used for this contribution.
  • AI fully generated the code for this contribution, but I've reviewed and understood it before submitting and will respond without AI during review.

Screenshots

Standard Mode (App bar & system status bar render untouched)

Standard Mode with Header Standard Mode System Bars

Fullscreen Mode (True edge-to-edge behind the hole punch)

Fullscreen True Edge-to-Edge

Safe Area Positioning (Standard HA UX and header remain properly positioned below safe area)

HA UX properly positioned below safe area

Any other notes

Empirical testing details and side-by-side hardware behavior across alert banners were documented in the follow-up comment on issue #7554.

- Configure layoutInDisplayCutoutMode = SHORT_EDGES in FullscreenEffect
- Remove artificial status bar overlay in SafeHAWebView
- Report real top safe area insets to frontend via pxToDp
- Obsolete frontend#29125 guardrail: modern Lovelace properly handles
  env(safe-area-inset-top) in CSS, eliminating the need for a native
  top letterbox strip while preserving drawer contrast in standard mode.

Fixes home-assistant#7554
Copilot AI balanced review requested due to automatic review settings October 13, 2026 03:02

@home-assistant home-assistant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @TalviFox

It seems you haven't yet signed a CLA. Please do so here.

Once you do that we will be able to review and accept this pull request.

Thanks!

@home-assistant
home-assistant Bot marked this pull request as draft October 13, 2026 03:02
@home-assistant

Copy link
Copy Markdown

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@TalviFox TalviFox changed the title Fix/hardcoded safe area fix(frontend): enable edge-to-edge display cutout in fullscreen mode Oct 13, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Legacy frontends receive neither the removed native top guardrail nor CSS safe-area insets, leaving content beneath the status bar.

1 open finding
What changed in this PR

Fixes fullscreen display-cutout letterboxing while forwarding the actual top safe-area inset to compatible frontends.

Changes:

  • Configures cutout layout mode with fullscreen state.
  • Removes the fixed top overlay and reports the real top inset.
File Description
LaunchActivity.kt Updates display-cutout mode during fullscreen changes.
FrontendScreen.kt Removes the top overlay and reports the top safe-area inset.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

val statusBarColor = contentState?.statusBarColor ?: fallbackColor
val backgroundColor = contentState?.backgroundColor ?: fallbackColor

Column(modifier = Modifier.fillMaxSize()) {
@TalviFox
TalviFox marked this pull request as ready for review October 13, 2026 03:23

@TimoPtr TimoPtr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not confident merging this.

val backgroundColor = contentState?.backgroundColor ?: fallbackColor

Column(modifier = Modifier.fillMaxSize()) {
// The top is never drawn edge-to-edge: we always reserve and color the status bar strip.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot simply remove it like this. We need to keep it for old servers with issues at the top. Also we did enable it at some point but doing so breaks on some screen not only on the drawer. It could be on apps for instance like Music Assistant that takes over the UI. We cannot remove it until we have a way to ensure that the frontend is not drawing a color that would hide the content of the status bar and today it's not guaranteed.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes = window.attributes.apply {
layoutInDisplayCutoutMode = if (isFullScreen) {
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you share a screenshot of the difference between the two?

@TimoPtr

TimoPtr commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

@jpelgrom it seems that it is enabled on iOS now with the issue and no complains so far (we might be over protective here?), but I still have some concerned that it might break at any moment of time.

Also this PR needs to be adjusted for old server.

Restore statusBarColor.Overlay on the top window insets when
serverHandleInsets is false. This ensures legacy Home Assistant servers
that do not consume raw insets continue to have their top status bar
protected, matching the behavior already in place for the left and
right window insets. Modern servers (> 2026.2) continue reporting
real insets via ReportSafeAreaInsetsEffect.
@TalviFox

Copy link
Copy Markdown
Author

@TimoPtr Updated.

Restored statusBarColor.Overlay inside if (!serverHandleInsets) in FrontendScreen.kt. Legacy servers that do not process safe-area insets continue to have their top status bar protected like before, matching how the sides are handled. Modern servers (> 2026.2) receive real insets via ReportSafeAreaInsetsEffect.

Here is the cutout comparison tested on a Pixel 9 Pro XL:

  • Without SHORT_EDGES, Android defaults to letterboxing the viewport, leaving large dead bars around the display cutout and navigation areas in immersive mode.
  • With SHORT_EDGES, dashboards seamlessly extend edge-to-edge across the entire display while remaining functional.

Fullscreen Immersive View

Default (Letterbox) Patched E2E (Menu Open) Patched E2E (Fullscreen)
Default-Screen-Behavior-Patched E2E-Fullscreen-Menu-Open-Patched E2E-Fullscreen-Patched

Card Dashboard View

Default (Letterbox) Patched E2E (Menu Open) Patched E2E (Header Hidden)
Default-Screen-Behavior-Card-Menu-Open E2E-Screen-Behavior-Card-Menu-Open E2E-Screen-Behavior-Card-Menu-Hidden

@jpelgrom

Copy link
Copy Markdown
Member

we might be over protective here?

Yes, that very well could be true. Edge to edge with all Android versions + fullscreen is just hard to get 100% correct.


  • What is meant with "Patched E2E (Menu Open)"? I do not see a menu in the screenshots.
  • Can you test with an app which has Ingress support ("Open web UI" button) and takes over the UI, like Music Assistant as mentioned here? If not maybe @TimoPtr can test this one :)

@jpelgrom jpelgrom changed the title fix(frontend): enable edge-to-edge display cutout in fullscreen mode Enable edge-to-edge display cutout in fullscreen mode Oct 14, 2026
@TalviFox

Copy link
Copy Markdown
Author

@jpelgrom

  • By "Patched", I meant the debug APK with this PR's code applied. "Menu Open" just referred to the top dashboard header / tab navigation bar being visible versus completely hidden in fullscreen.
  • I'll see if I can get some Ingress testing done on my end as well, but @TimoPtr should probably verify with Music Assistant too just to have multiple eyes and devices on it. ;)

@TalviFox

Copy link
Copy Markdown
Author

@jpelgrom Tested Music Assistant on the Pixel 9 Pro XL as well: safe areas and layout hold up cleanly in fullscreen. Top header controls and bottom player bar remain fully accessible with no cutout collisions.

Screenshot_20261014-172534 Screenshot_20261014-172545

@TalviFox

Copy link
Copy Markdown
Author

Also tested rapid screen rotation in Music Assistant:

Rotating between landscape and portrait does show standard WebView viewport re-layout latency (padding shifts slightly until re-focused), but crucially, no interactive UI elements or buttons are ever permanently clipped or rendered behind the camera punch-hole. It degrades safe rather than destructive.

The empty area shows up left or right depending on the the previous rotation.

Screenshot_20261014-174111 Screenshot_20261014-174114 Screenshot_20261014-174121 Screenshot_20261014-174125

@TimoPtr TimoPtr added the wait next stable release Wait until the next stable release is widely available, because extended testing/review is needed label Oct 15, 2026
.padding(insets.only(WindowInsetsSides.Horizontal).asPaddingValues()),
)
// The top status bar guardrail is retained for legacy servers (< 2026.2) where the frontend does not handle safe areas.
if (!serverHandleInsets) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I think this is not enough from what I remember at the moment we've introduced this flag it was not working properly so we should enable this starting on the release you've tested, I know @bgoncal fixed a lot of issues in the frontend recently about safe area. I would gate this to 2026.10 to be safe @jpelgrom any opinion.

Testing this is always painful since we have so many screen on the frontend, I want this in the next beta so we can have feedbacks before a release in prod like we did last time (that led us to keep the top bar).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed wait next stable release Wait until the next stable release is widely available, because extended testing/review is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fullscreen mode does not extend into display cutout / camera hole punch (leaves black bar)

5 participants