Render between the system bars deterministically; delete my inset bridge
The app sometimes drew under the status and gesture bars and sometimes between them. Cause: Capacitor 8 ships a SystemBars plugin that already handles insets, from a listener on the decor view, and my MainActivity had installed another one on the WebView. Different views, so they stacked rather than replaced each other and the result depended on which fired last. SystemBars switches on the viewport meta tag. With viewport-fit=cover it passes insets through once onPageCommitVisible has checked the tag, decor padding drops to 0 and the WebView grows 735 -> 820, leaving the page to place itself from injected CSS vars. Without it the plugin pads the decor view and the WebView is laid out between the bars already. So sync-www.sh now strips viewport-fit=cover from the generated www/ (patch 5), MainActivity is an empty BridgeActivity, native.css and native.js no longer touch insets, and bar style moves to capacitor.config.json. No native->web handshake remains, so there is nothing to race and no launch-time layout jump. Verified identical across 3 cold launches, 2 resumes and an in-place reload: h=735 padTop=8px overflowBy=0, panel 485px, pad rows 44.8px. Costs no space: 820-45-114 and 735-8-66 both leave 661px. The bars take the same room either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
46
CLAUDE.md
46
CLAUDE.md
@@ -117,16 +117,37 @@ code about to be rewritten in React anyway.
|
||||
Explore → go to Explore; otherwise exit. Registering a listener suppresses
|
||||
Capacitor's default history navigation.
|
||||
|
||||
**Edge-to-edge is handled natively, not avoided.** Android 16 is API 36 and
|
||||
Capacitor 8 targets SDK 36, which is exactly the combination where
|
||||
`windowOptOutEdgeToEdgeEnforcement` stops working. An Android WebView never
|
||||
populates `env(safe-area-inset-*)`, so `MainActivity` installs an
|
||||
`OnApplyWindowInsetsListener`, converts px → dp, and pushes the values into
|
||||
`--ins-t/r/b/l` via `window.__applyInsets()`. `native.css` re-points the
|
||||
reference's `--sab` at `--ins-b`, so `.bnav` and `.sheet` need no changes.
|
||||
**System bars: let Capacitor do it, by dropping `viewport-fit=cover`.** The app
|
||||
renders between the status and gesture bars, always.
|
||||
|
||||
The push retries on a bounded schedule (25 × 200ms) because insets typically
|
||||
arrive before the page has run any JS.
|
||||
Capacitor 8 ships a `SystemBars` plugin that owns inset handling from a listener
|
||||
on the **decor view**. Its behaviour hinges on the viewport meta tag:
|
||||
|
||||
- **with `viewport-fit=cover`** (what `reference/` has), `shouldPassthroughInsets`
|
||||
becomes true once `onPageCommitVisible` has checked the tag and called
|
||||
`requestApplyInsets()`. Decor padding drops to 0, the WebView grows from 735 to
|
||||
820, and the page must place itself from injected CSS variables.
|
||||
- **without it**, the plugin pads the decor view instead. The WebView is 735px
|
||||
and already sits between the bars; `env(safe-area-inset-*)` and the
|
||||
reference's `--sab` are 0, and `.bnav` and `.sheet` are correct as written.
|
||||
|
||||
So `sync-www.sh` strips `viewport-fit=cover` from the generated `www/` (patch 5).
|
||||
No native→web handshake exists, so there is nothing to race and no launch-time
|
||||
layout jump. Verified identical across cold launch, resume and in-place reload:
|
||||
`h=735 padTop=8px overflowBy=0`.
|
||||
|
||||
**This costs no space.** 820 − 45 − 114 and 735 − 8 − 66 both leave 661px, because
|
||||
the bars take the same room whether the WebView is padded or draws underneath.
|
||||
Reclaiming that 85px would mean *hiding* the bars, which is a different choice.
|
||||
|
||||
An earlier version did the opposite: `MainActivity` installed its own
|
||||
`OnApplyWindowInsetsListener` on the WebView and pushed `--ins-t/r/b/l` through
|
||||
`window.__applyInsets()`, with a 25 × 200ms retry. That was written without
|
||||
realising Capacitor already had a plugin for this. The two listeners were on
|
||||
different views, so they stacked rather than replaced each other, and the app
|
||||
drew under the bars at random depending on which fired last. `MainActivity` is
|
||||
now empty and bar styling is configured in `capacitor.config.json`
|
||||
(`SystemBars.style: DARK`).
|
||||
|
||||
**No community plugins.** Only first-party: `@capacitor/core`, `/cli`,
|
||||
`/android`, `/haptics`, `/preferences`, `/app`.
|
||||
@@ -176,9 +197,14 @@ function rewrites `#strip` wholesale.
|
||||
|
||||
`cap sync` does not regenerate these — keep them when recreating `android/`:
|
||||
|
||||
- `android/app/src/main/java/dev/ncode/sp404layers/MainActivity.java` — inset bridge
|
||||
- `android/app/src/main/AndroidManifest.xml` — `android:screenOrientation="portrait"`
|
||||
|
||||
That is the only one. `MainActivity.java` is deliberately an empty
|
||||
`BridgeActivity`: everything it used to do is either configured in
|
||||
`capacitor.config.json` or handled by Capacitor's own `SystemBars` plugin. If
|
||||
you find yourself adding an inset listener there, read the system-bars decision
|
||||
above first — that path has already been tried and reverted.
|
||||
|
||||
## Device notes (TCL 50 PRO NXTPAPER 5G, Android 16 / API 36)
|
||||
|
||||
**adb needs a udev rule.** The phone is USB `1bbb:0c01`. Ubuntu's
|
||||
|
||||
Reference in New Issue
Block a user