From fa7dcb18be25232cf91639c1304abadbd0ba0f89 Mon Sep 17 00:00:00 2001 From: SimpleTest Date: Fri, 24 Jul 2026 18:59:12 +0300 Subject: [PATCH] fix(android): respect system bar insets --- .../SystemBarInsetsInstrumentedTest.java | 42 +++++++++++++++ .../org/whoneedhelp/mobile/MainActivity.java | 51 ++++++++++++++++++- android/app/src/main/res/values/ids.xml | 4 ++ android/app/src/main/res/values/styles.xml | 2 +- 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 android/app/src/androidTest/java/org/whoneedhelp/mobile/SystemBarInsetsInstrumentedTest.java create mode 100644 android/app/src/main/res/values/ids.xml diff --git a/android/app/src/androidTest/java/org/whoneedhelp/mobile/SystemBarInsetsInstrumentedTest.java b/android/app/src/androidTest/java/org/whoneedhelp/mobile/SystemBarInsetsInstrumentedTest.java new file mode 100644 index 0000000..ccc671b --- /dev/null +++ b/android/app/src/androidTest/java/org/whoneedhelp/mobile/SystemBarInsetsInstrumentedTest.java @@ -0,0 +1,42 @@ +package org.whoneedhelp.mobile; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.os.Build; +import android.view.View; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public final class SystemBarInsetsInstrumentedTest { + @Rule + public final ActivityScenarioRule activityRule = + new ActivityScenarioRule<>(MainActivity.class); + + @Test + public void edgeToEdgeContentUsesSystemBarInsets() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + return; + } + + activityRule.getScenario().onActivity(activity -> { + View contentView = activity.findViewById(R.id.main_content); + + assertNotNull(contentView); + assertTrue( + "The app content must start below the status bar", + contentView.getPaddingTop() > 0 + ); + assertTrue( + "The app content must end above the navigation bar", + contentView.getPaddingBottom() > 0 + ); + }); + } +} diff --git a/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java b/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java index a3560a9..41270ee 100644 --- a/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java +++ b/android/app/src/main/java/org/whoneedhelp/mobile/MainActivity.java @@ -13,6 +13,7 @@ import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.util.Log; +import android.view.View; import android.view.ViewGroup; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; @@ -24,6 +25,7 @@ import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.FrameLayout; import android.widget.Toast; import androidx.activity.ComponentActivity; @@ -39,6 +41,9 @@ import androidx.credentials.GetCredentialResponse; import androidx.credentials.exceptions.ClearCredentialException; import androidx.credentials.exceptions.GetCredentialException; import androidx.credentials.exceptions.NoCredentialException; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.webkit.WebMessageCompat; import androidx.webkit.WebViewCompat; import androidx.webkit.WebViewFeature; @@ -132,7 +137,17 @@ public final class MainActivity extends ComponentActivity { ViewGroup.LayoutParams.MATCH_PARENT ) ); - setContentView(webView); + FrameLayout contentView = new FrameLayout(this); + contentView.setId(R.id.main_content); + contentView.setLayoutParams( + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + ); + contentView.addView(webView); + setContentView(contentView); + applySystemBarInsets(contentView); WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG); @@ -176,6 +191,40 @@ public final class MainActivity extends ComponentActivity { } } + private void applySystemBarInsets(View contentView) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + return; + } + + ViewCompat.setOnApplyWindowInsetsListener(contentView, (view, windowInsets) -> { + Insets safeInsets = windowInsets.getInsets( + WindowInsetsCompat.Type.systemBars() + | WindowInsetsCompat.Type.displayCutout() + ); + view.setPadding( + safeInsets.left, + safeInsets.top, + safeInsets.right, + safeInsets.bottom + ); + if (PAGE_LOAD_DIAGNOSTICS) { + Log.d( + LOG_TAG, + "Applied system bar insets: left=" + + safeInsets.left + + ", top=" + + safeInsets.top + + ", right=" + + safeInsets.right + + ", bottom=" + + safeInsets.bottom + ); + } + return windowInsets; + }); + ViewCompat.requestApplyInsets(contentView); + } + private String initialUrl() { return LaunchUrlResolver.initialUrl( trustedOrigin, diff --git a/android/app/src/main/res/values/ids.xml b/android/app/src/main/res/values/ids.xml new file mode 100644 index 0000000..6e12d86 --- /dev/null +++ b/android/app/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 8a708e7..06730ff 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -6,6 +6,6 @@ @color/brand_green_dark @color/brand_green true - false + true