super-productivity/android/app
Johannes Millan 87846ad83d
fix(android): keyboard + status-bar follow-ups for edge-to-edge (#8508) (#8548)
* fix(android): size fullscreen markdown/notes dialog above the keyboard (#8508)

The fullscreen markdown editor (project & task notes) is position:fixed;
height:100% but its keyboard rule subtracted --keyboard-overlay-offset, which
is set only on iOS. On Android the rule was a no-op, so with the IME open the
dialog kept full height (content behind the keyboard) or inherited the squashed
sliver, leaving the toolbar + textarea + Close/Save mashed to the top.

Use the resize-detecting --keyboard-height for the Android/mobile-web case
(0 once the window resized, the obscured amount otherwise), mirroring the
add-task bar and .app-container; keep the iOS --keyboard-overlay-offset path as
a second, source-order-later rule (equal specificity, wins on iOS). On a device
that resizes (--keyboard-height == 0) it is identical to the old rule, so it is
never worse than before; it fixes the API >= 30 no-resize/VisualViewport-shrink
case and composes with the SDK < 30 native fix (#8528).

* fix(android): inset the header below the status bar on API < 30 (#8508)

Under enforced edge-to-edge (targetSdk 36) the WebView extends under the status
bar, but on the API < 30 WebView `env(safe-area-inset-top)` resolves to 0 (old
WebViews map only display cutouts into safe-area insets, not the status bar), so
`--safe-area-top` was 0 and the web header overlapped the status bar on Android 9.

The web side cannot tell "edge-to-edge under the status bar" from "already
natively inset" (env() is 0 in both), so a web-only fallback would double-count.
Measure the overlap natively instead: in the existing keyboard layout listener
(gated SDK < 30) compute max(0, rect.top - webViewTopOnScreen) — visible-frame
top (status-bar height, reliable on API 28) minus the WebView's on-screen top (0
when edge-to-edge, == status-bar height once inset) — and publish it as the
`--android-status-bar-overlap` CSS var (physical px -> CSS px, deduped). The web
folds it in with max(env(safe-area-inset-top, 0px), var(--android-status-bar-
overlap, 0px)): never a sum so it can't double-count, and on API >= 30 the var is
never set so `max(env, 0) = env` keeps the verified behavior. JS readers still
parse the token to 0, preserving the #8283 overlay scoping.

Needs on-device validation on API < 30 and a no-op check on API >= 30.

* fix(android): stop double-counting safe-area-top in note dialog height (#8508)

The fullscreen note/markdown dialog subtracted --safe-area-top from its
keyboard-open height while ALSO applying padding-top: --safe-area-top. Since
:host is border-box (global * { box-sizing: border-box }), the padding is already
inside height: 100%, so subtracting --safe-area-top again double-removed the top
inset and left a --safe-area-top-sized gap between the Close/Save controls and
the keyboard.

Invisible while --safe-area-top was 0 on API < 30; it surfaced once the
status-bar fix made it non-zero (and was latent on API >= 30 where env() already
gave a non-zero value). Drop the - --safe-area-top term from the Android rule so
the controls sit flush above the keyboard while the toolbar stays below the
status bar. iOS keeps its own override (different keyboard runtime, unverified on
device) and is flagged for separate checking.

* fix(android): re-publish status-bar overlap after web reload (#8508)

Review follow-ups to the #8508 keyboard/status-bar fixes:

- The native --android-status-bar-overlap lives only as an inline style on
  the document, so a web-side window.location.reload() (language change, PWA
  update, sync-conflict recovery) wipes it while the dedupe field survives on
  the Activity -> the unchanged value is skipped and the header overlaps the
  status bar again on the WebView < 140 / API < 30 tail. Reset
  lastStatusBarOverlapCssPx in flushPendingShareIntent() (runs on every
  frontend (re)load) so the next layout pass re-publishes it.

- Dialog keyboard rule: scope the Android rule with :not(.isIOS). iOS carries
  both isNativeMobile and isIOS and sets --keyboard-height non-zero, so the two
  equal-specificity rules both matched and iOS correctness depended on source
  order; they are now mutually exclusive and order-independent.

- Declare --android-status-bar-overlap: 0px in :root for discoverability,
  alongside the other dynamic vars.
2026-06-23 11:57:49 +02:00
..
src fix(android): keyboard + status-bar follow-ups for edge-to-edge (#8508) (#8548) 2026-06-23 11:57:49 +02:00
.gitignore ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
app_config.properties ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
build.gradle 18.12.1 2026-06-22 13:37:54 +02:00
capacitor.build.gradle feat(android): migrate edge-to-edge to built-in SystemBars (#8543) 2026-06-22 16:07:06 +02:00
config.gradle ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
google.properties ____MERGING SUPER PRODUCTIVITY ANDROID ____ 2024-10-11 14:11:23 +02:00
proguard-rules.pro feat(droid): background sync notifications for android 2026-03-20 10:24:02 +01:00