Skip to content

fix(core): android edge to edge stabilization - #11434

Open
triniwiz wants to merge 7 commits into
mainfrom
fix/android-edge-to-edge-stabilization
Open

triniwiz wants to merge 7 commits into
mainfrom
fix/android-edge-to-edge-stabilization

Conversation

@triniwiz

Copy link
Copy Markdown
Member

PR Checklist

What is the current behavior?

Seven defects in androidOverflowEdge, all reproduced on an Android 16 device (SM-A536E, SDK 36): ignore was missing from the edge map so ignore,bottom became plain bottom; a value resolving to none (none,none) parsed as "leave the view alone" and became a no-op; switching back to ignore never gave back the padding already baked in; setPadding re-added only the navigation bar inset, so doing it with the keyboard open collapsed the keyboard gap; removing one androidOverflowInset subscriber killed the native listener for all of them, and a recycled native view never got it back; and the system bar icon appearance was never re-applied after a dark-mode switch, on the activity's window or on an open fullscreen modal's.

Separately, insets came from Type.systemBars() alone. In portrait that coincides with the display cutout so nobody notices, but in landscape the camera moves to an edge with no bar and none let content slide under it.

What is the new behavior?

All seven fixed, plus a cutout token that appends to any value (none,cutout, top,cutout) to fold displayCutout() into the insets being distributed — opt-in, since folding it in everywhere would silently add padding to landscape layouts that already work around this. Also drops the shortcut that returned WindowInsetsCompat.CONSUMED when a pass reported no system bars — it skipped the padding reset and never notified dont-apply listeners. parseEdges moves to an internal module so it can be unit tested (9 new cases), and org.nativescript.widgets.d.ts picks up OverflowEdgeIgnore, OverflowEdgeCutout, getEdgeInsets, getImeInsets and setInsetListener.

apps/toolbox gains an edge-to-edge suite under pages/ete (ete/hub on the main page). Each page paints the padding a view applied as a coloured band and prints the insets it measured beside it, so a wrong value shows up in a screenshot instead of having to be traced. Verified on device across all 17 androidOverflowEdge values in portrait 3-button, portrait gesture nav and landscape, plus keyboard insets, modals, and live theme switches both on a page and with a fullscreen modal open.

NathanWalker and others added 7 commits September 3, 2026 10:35
…ange

Core enables edge-to-edge once, in onActivityCreated, with the auto system
bar style. The activity survives a dark-mode switch (uiMode is a handled
configuration change), so the status and navigation bar icons keep the
appearance chosen for the previous theme: dark icons over the app's dark
canvas after switching to night mode.

Add `refreshEdgeToEdge`, which re-applies the activity's styling with any
colors or handler registered through the existing setters, and call it
from the Android application when the system appearance changes.
Five defects in how androidOverflowEdge distributes window insets, all
reproduced on a real Android 16 device (SM-A536E, SDK 36):

- `ignore` was missing from the edge map, so a stacked value like
  `ignore,bottom` silently degraded to plain `bottom`.
- parseEdges returned null for any value resolving to none (`none,none`),
  and null means "leave the view alone" - so the property became a no-op
  that stranded the view on whatever edges it had before.
- A view switched back to `ignore` never gave back the padding it had
  baked in: the listener stays installed but passes insets straight
  through from then on, so nothing undid the earlier pass.
- setPadding re-added only edgeInsets.bottom, while the inset pass had
  applied max(navigation bar, ime). Any setPadding while the keyboard was
  open collapsed the keyboard gap.
- Removing one androidOverflowInset subscriber tore down the native
  listener for all of them, and disposeNativeView left insetListenerIsSet
  true, so a recycled native view never got the listener back and the
  event went quiet after navigating away and returning.

Also drops the shortcut that returned WindowInsetsCompat.CONSUMED when a
pass reported no system bars: it skipped the padding reset and never
notified `dont-apply` listeners when the bars went away.

parseEdges moves to its own internal module so it can be unit tested
without the whole view stack, and org.nativescript.widgets.d.ts picks up
OverflowEdgeIgnore, getEdgeInsets, getImeInsets and setInsetListener.
Seven pages under pages/ete, reachable from the ete/hub entry on the main
page. Each one is readable from a screenshot alone: a shared colour legend
means the ring you see around the content IS the padding the view applied,
so a wrong inset shows up as a wrong-sized band rather than a number
someone has to go looking for.

- edges     walks every androidOverflowEdge value, including the stacked
            `none,none` and `ignore,bottom` regressions
- nested    dont-apply parent whose JS handler rewrites and consumes the
            insets, with a child that pads by what survived
- ime       keyboard insets, and setPadding while the keyboard is open
- lifecycle the ignore reset sequence, and whether the inset event still
            fires after the native view has been recycled
- modals    fullscreen vs floating modal windows
- bars      statusBarStyle, bar colours, and live system theme switches
- scroll    the everyday pattern: list scrolling under the navigation bar

Pages that let the probe overflow keep their controls out of the
navigation bar's reach - a bottom-pinned button there is not tappable.
The bar icon appearance is chosen from the theme when the style is
applied, and the activity survives a dark mode switch, so without this
the bars keep the old theme's icons until the app restarts. Confirmed on
an Android 16 device before merging: a live switch to light mode left the
navigation bar dark-on-light-icons, while a fresh start in light mode got
it right.
Insets came from Type.systemBars() alone, which leaves the display cutout
out. In portrait the two coincide and nobody notices, but rotate the
device and the camera moves to an edge that has no bar - so `none`, which
reads as "keep my content clear of the system UI", let content slide
under it.

`cutout` appends to any existing value (`none,cutout`, `top,cutout`) and
folds displayCutout() into the insets being distributed. It is a modifier
rather than an edge: the bit is masked off before anything asks which
edges were requested, so it composes with the whole vocabulary instead of
multiplying it. Whatever is consumed of the cutout is withheld from
children too, so a nested view that also asked for it does not apply it
twice.

Left opt-in rather than made the default: folding the cutout in
everywhere would silently add padding to landscape layouts that have
already worked around this.

Measured on an Android 16 device in landscape, where systemBars is
0,84,0,42 and the cutout is 88,0,0,0: `none` pads 0,84,0,42 and
`none,cutout` pads 88,84,0,42.
…ange

refreshEdgeToEdge re-styles the activity's window, but a fullscreen modal
has its own dialog window styled separately when it opens. Nothing
re-applied that one, so a modal open across a dark-mode switch kept the
bar icons of whichever theme was active when it was shown - white icons
on a light navigation bar, and the reverse switching back.

The dialog fragment now watches its own configuration changes and
re-applies when the night mode bit actually moves, so the cost is one
comparison per configuration change rather than a restyle on every
rotation.

Verified on an Android 16 device with a fullscreen modal open across a
switch in both directions.
@nx-cloud

nx-cloud Bot commented Sep 15, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 62c48f4

Command Status Duration Result
nx test apps-automated -c=android ✅ Succeeded 3m 51s View ↗
nx run-many --target=test --configuration=ci --... ✅ Succeeded <1s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-09-15 17:15:00 UTC

@pkg-pr-new

pkg-pr-new Bot commented Sep 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@nativescript/core@11434
npm i https://pkg.pr.new/@nativescript/vite@11434
npm i https://pkg.pr.new/@nativescript/webpack@11434

commit: 62c48f4

@triniwiz triniwiz changed the title Fix/android edge to edge stabilization fix(core): android edge to edge stabilization Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants