mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
fix(android): push header below status bar on API 30-34 with old WebView (#8283)
The native status-bar overlap fallback (pushStatusBarOverlap) was gated to SDK < 30, but the band that needs it is WebView < 140 AND API < 35: there SystemBars injects nothing and env(safe-area-inset-top) resolves to 0, so the header draws behind the status bar. API 30-34 devices on an un-updated WebView (e.g. de-Googled LineageOS/microG, which doesn't refresh System WebView via Play) were uncovered. Broaden the gate SDK < 30 -> SDK < 35 and rename the method accordingly. The WebView >= 140 early-return still skips the passthrough band, and the max(env(), overlap) fold self-corrects to 0 when the WebView is natively inset, so there is no double-count and the worst case is no change.
This commit is contained in:
parent
6236dc7458
commit
ada565c651
2 changed files with 25 additions and 22 deletions
|
|
@ -57,9 +57,9 @@ class CapacitorMainActivity : BridgeActivity() {
|
|||
// path) to avoid allocating an IntArray on every pass while the IME is up.
|
||||
private val webViewLocationOnScreen = IntArray(2)
|
||||
|
||||
// SDK < 30 status-bar overlap workaround: last value pushed to JS, to dedupe
|
||||
// SDK < 35 status-bar overlap workaround: last value pushed to JS, to dedupe
|
||||
// the per-layout-pass listener. -1 = nothing pushed yet.
|
||||
// See pushStatusBarOverlapBelowApi30.
|
||||
// See pushStatusBarOverlapBelowApi35.
|
||||
private var lastStatusBarOverlapCssPx: Int = -1
|
||||
|
||||
private var isTimerCompleteReceiverRegistered = false
|
||||
|
|
@ -223,7 +223,7 @@ class CapacitorMainActivity : BridgeActivity() {
|
|||
if (isKeyboardOpen) "true" else "false"
|
||||
)
|
||||
adjustWebViewHeightForKeyboardBelowApi30(rect, isKeyboardOpen)
|
||||
pushStatusBarOverlapBelowApi30(rect)
|
||||
pushStatusBarOverlapBelowApi35(rect)
|
||||
}
|
||||
|
||||
// Register broadcast receiver for focus mode timer completion
|
||||
|
|
@ -348,8 +348,8 @@ class CapacitorMainActivity : BridgeActivity() {
|
|||
// re-enters here, but it also wipes the inline --android-status-bar-overlap
|
||||
// off the fresh document. Re-arm the dedupe so the next layout pass
|
||||
// re-publishes it; otherwise the unchanged value is skipped and the
|
||||
// header overlaps the status bar again on the WebView < 140 / API < 30
|
||||
// tail. See pushStatusBarOverlapBelowApi30.
|
||||
// header overlaps the status bar again on the WebView < 140 / API < 35
|
||||
// tail. See pushStatusBarOverlapBelowApi35.
|
||||
lastStatusBarOverlapCssPx = -1
|
||||
pendingShareIntent?.let {
|
||||
Log.d("SP_SHARE", "Flushing pending share intent: $it")
|
||||
|
|
@ -549,37 +549,39 @@ class CapacitorMainActivity : BridgeActivity() {
|
|||
|
||||
/**
|
||||
* Status-bar overlap workaround for the web header drawing BEHIND the status
|
||||
* bar on the WebView < 140 tail (#8508 / #8283 follow-up, Android 9 / API 28).
|
||||
* bar on the WebView < 140 tail (#8508 / #8283, Android 9 / API 28 through
|
||||
* Android 14 / API 34).
|
||||
*
|
||||
* Edge-to-edge insets are owned by Capacitor's built-in SystemBars now. On
|
||||
* **API >= 35** it injects the real `--safe-area-inset-*` px, and on
|
||||
* **WebView >= 140** the WebView's own `env(safe-area-inset-*)` is correct
|
||||
* (passthrough). But on the **WebView < 140** tail under enforced edge-to-edge
|
||||
* the WebView extends under the status bar while `env(safe-area-inset-top)`
|
||||
* resolves to 0 (old WebViews map only display cutouts into safe-area insets,
|
||||
* not the status bar) — so the web side has no top inset and content overlaps
|
||||
* the status bar.
|
||||
* (passthrough). But on the **WebView < 140 / API < 35** tail under enforced
|
||||
* edge-to-edge the WebView extends under the status bar while
|
||||
* `env(safe-area-inset-top)` resolves to 0 (old WebViews map only display
|
||||
* cutouts into safe-area insets, not the status bar) — so the web side has no
|
||||
* top inset and content overlaps the status bar.
|
||||
*
|
||||
* We measure the overlap natively and publish it as the `--android-status-bar-
|
||||
* overlap` CSS var, which the web side folds into `--safe-area-top` via
|
||||
* `var(--safe-area-inset-top, max(env(...), var(--android-status-bar-overlap)))`.
|
||||
* The overlap is how much of the status bar covers the WebView: `rect.top`
|
||||
* (top of the visible display frame = status-bar height, reliable on API 28,
|
||||
* (top of the visible display frame = status-bar height, reliable API 28–34,
|
||||
* the same frame the keyboard path uses) minus the WebView's top on screen
|
||||
* (`getLocationOnScreen`: 0 when edge-to-edge, == status-bar height once
|
||||
* inset). So it is the status-bar height when the WebView is NOT inset and 0
|
||||
* once it is — `max()` never double-counts. Physical px → CSS px via display
|
||||
* density; deduped so the per-layout listener does not spam evaluateJavascript.
|
||||
*
|
||||
* Gated to **SDK < 30 AND WebView < 140** (mirrors
|
||||
* adjustWebViewHeightForKeyboardBelowApi30) so it never fights SystemBars; on
|
||||
* API >= 35 the injected --safe-area-inset-top wins via var() precedence and
|
||||
* the published var is ignored regardless. (Known small gap: an API 30–34
|
||||
* device on an old WebView < 140 also has env()==0; rare, since WebView
|
||||
* auto-updates above API 30 — broaden the gate if it ever surfaces.)
|
||||
* Gated to **SDK < 35 AND WebView < 140** so it never fights SystemBars: on
|
||||
* API >= 35 SystemBars injects --safe-area-inset-top (wins via var()
|
||||
* precedence), and WebView >= 140 has correct env() passthrough. The full
|
||||
* API < 35 range (not just < 30) is required because API 30–34 devices on an
|
||||
* un-updated WebView < 140 — e.g. de-Googled ROMs where the system WebView is
|
||||
* not refreshed via Play — also report env()==0 (#8283, Pixel 7a / Android 14
|
||||
* / LineageOS-microG).
|
||||
*/
|
||||
private fun pushStatusBarOverlapBelowApi30(rect: Rect) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 30) return
|
||||
private fun pushStatusBarOverlapBelowApi35(rect: Rect) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 35) return
|
||||
// Skip when SystemBars/env() already give the correct top inset (WebView
|
||||
// >= 140 passthrough). Unknown version (null) -> run it, the safe default.
|
||||
val wvMajor = webViewCompatibility?.majorVersion
|
||||
|
|
|
|||
|
|
@ -48,10 +48,11 @@
|
|||
// Uses env() for native iOS safe areas
|
||||
// -----------------------------
|
||||
// `--android-status-bar-overlap` is fed natively (CapacitorMainActivity
|
||||
// .pushStatusBarOverlapBelowApi30) on the WebView < 140 tail, where
|
||||
// .pushStatusBarOverlapBelowApi35) on the WebView < 140 / API < 35 tail, where
|
||||
// env(safe-area-inset-top) wrongly resolves to 0 under edge-to-edge (old
|
||||
// WebViews map only display cutouts into safe-area insets, not the status bar)
|
||||
// — #8508 / #8283 (Android 9 / API 28). It is 0 once the WebView is inset, so
|
||||
// — #8508 / #8283 (Android 9 / API 28 through Android 14 / API 34). It is 0
|
||||
// once the WebView is inset, so
|
||||
// max() never double-counts; on API >= 35 / WebView >= 140 the injected
|
||||
// --safe-area-inset-top wins via var() precedence and this fallback is ignored.
|
||||
// Declared 0px so the token is always defined (it is only ever overridden by
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue