From 6e8cac6fe6ce2a77b0a0caa91a2dba9329894e22 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 19 Aug 2026 10:34:58 -0300 Subject: [PATCH] Fix toggling fullscreen system UI on API 29 and below. --- .../securesms/util/FullscreenHelper.java | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FullscreenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/util/FullscreenHelper.java index 98db230b80..18d32c7d27 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FullscreenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FullscreenHelper.java @@ -91,32 +91,40 @@ public final class FullscreenHelper { return new int[]{leftPad, rightPad}; } + @SuppressWarnings("deprecation") public void showAndHideWithSystemUI(@NonNull Window window, @NonNull View... views) { - ViewCompat.setOnApplyWindowInsetsListener(window.getDecorView(), (view, insets) -> { - boolean hide = !areBarsVisible(insets); + if (Build.VERSION.SDK_INT >= 30) { + ViewCompat.setOnApplyWindowInsetsListener(window.getDecorView(), (view, insets) -> { + animateWithBars(!areBarsVisible(insets), views); + return ViewCompat.onApplyWindowInsets(view, insets); + }); + } else { + window.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> { + animateWithBars((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0, views); + }); + } + } - for (View target : views) { - if (target == null) { - continue; - } - - target.animate() - .alpha(hide ? 0 : 1) - .withStartAction(() -> { - if (!hide) { - target.setVisibility(View.VISIBLE); - } - }) - .withEndAction(() -> { - if (hide) { - target.setVisibility(View.INVISIBLE); - } - }) - .start(); + private static void animateWithBars(boolean hide, @NonNull View... views) { + for (View target : views) { + if (target == null) { + continue; } - return ViewCompat.onApplyWindowInsets(view, insets); - }); + target.animate() + .alpha(hide ? 0 : 1) + .withStartAction(() -> { + if (!hide) { + target.setVisibility(View.VISIBLE); + } + }) + .withEndAction(() -> { + if (hide) { + target.setVisibility(View.INVISIBLE); + } + }) + .start(); + } } public void toggleUiVisibility() { @@ -127,13 +135,23 @@ public final class FullscreenHelper { } } + @SuppressWarnings("deprecation") public boolean isSystemUiVisible() { - return areBarsVisible(ViewCompat.getRootWindowInsets(activity.getWindow().getDecorView())); + if (Build.VERSION.SDK_INT >= 30) { + return areBarsVisible(ViewCompat.getRootWindowInsets(activity.getWindow().getDecorView())); + } else { + return (activity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0; + } } /** * Whether the bars that {@link #showSystemUI()} / {@link #hideSystemUI()} control are currently on screen. *

+ * Only usable on API 30+. Below that, {@link WindowInsetsCompat#isVisible(int)} is emulated off of the inset + * sizes, and because we're edge-to-edge (and therefore laid out with {@code SYSTEM_UI_FLAG_LAYOUT_STABLE}) + * those stay at their full height while the bars are hidden, making everything look permanently visible. + * The legacy visibility flags are the reliable signal there. + *

* Checks the two bars individually rather than {@link WindowInsetsCompat.Type#systemBars()}, which also * covers the caption bar: {@code isVisible} requires every requested type to be visible, and a phone window * has no caption bar source, so the aggregate answer is always "hidden".