From 999ccf46d6b98ef1dc17ea2b5d90ed938024d92e Mon Sep 17 00:00:00 2001 From: Priveetee Date: Tue, 16 Jun 2026 08:18:59 +0200 Subject: [PATCH 1/3] fix(ui): apply bottom-sheet system-window insets so the mini player clears the nav bar --- .../fragments/detail/VideoDetailFragment.java | 15 --------------- app/src/main/res/layout/activity_main.xml | 1 + 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 10ed4fb44e..9baf6cffba 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -722,21 +722,6 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) { pageAdapter = new TabAdapter(getChildFragmentManager()); binding.viewPager.setAdapter(pageAdapter); binding.tabLayout.setupWithViewPager(binding.viewPager); - // Edge-to-edge (targetSdk 35+): the bottom tab strip is laid out gravity=bottom, so without - // insets it sits behind the system navigation bar (tabs unclickable) and the pager's last - // item runs under it. Pad the tab strip by the nav-bar inset and grow the pager's bottom - // padding (its XML 48dp tab clearance) by the same amount so content clears both. - final int viewPagerBottomBase = binding.viewPager.getPaddingBottom(); - ViewCompat.setOnApplyWindowInsetsListener(binding.tabLayout, (v, insets) -> { - final int navBottom = insets.getInsets( - WindowInsetsCompat.Type.navigationBars()).bottom; - v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(), navBottom); - binding.viewPager.setPadding(binding.viewPager.getPaddingLeft(), - binding.viewPager.getPaddingTop(), binding.viewPager.getPaddingRight(), - viewPagerBottomBase + navBottom); - return insets; - }); - ViewCompat.requestApplyInsets(binding.tabLayout); updateStickyPlayerMode(); binding.detailThumbnailRootLayout.requestFocus(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 97ccd199ec..1d60727009 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -25,6 +25,7 @@ android:layout_gravity="center_horizontal" app:behavior_hideable="true" app:behavior_peekHeight="0dp" + app:paddingBottomSystemWindowInsets="true" app:layout_behavior="org.schabi.newpipe.player.event.CustomBottomSheetBehavior" /> From cd22fc6646bb87e1c6456497291120894bf221f5 Mon Sep 17 00:00:00 2001 From: Priveetee Date: Wed, 17 Jun 2026 13:42:52 +0200 Subject: [PATCH 2/3] fix(ui): pad the main content holder by the navigation-bar inset so lists clear the nav --- .../java/org/schabi/newpipe/MainActivity.java | 3 +++ .../schabi/newpipe/fragments/MainFragment.java | 16 ---------------- .../newpipe/util/WindowInsetsHelper.java | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 0586fed665..3894c1fabc 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -154,6 +154,9 @@ protected void onCreate(final Bundle savedInstanceState) { WindowInsetsHelper.applyStatusBarInsets(this, toolbarLayoutBinding.toolbar, mainBinding.fragmentHolder); + // Edge-to-edge (targetSdk 35+): keep the main content (feed/search/channel/playlist lists, + // and the bottom tab strip) above the system navigation bar. + WindowInsetsHelper.applyNavigationBarInsets(mainBinding.fragmentHolder); if (getSupportFragmentManager().getBackStackEntryCount() == 0) { initFragments(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 5f10b41820..e370868887 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -24,8 +24,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround; @@ -107,17 +105,6 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) { binding.mainTabLayout.setTabRippleColor(binding.mainTabLayout.getTabRippleColor() .withAlpha(32)); - // Edge-to-edge (targetSdk 35+): when the tab strip is moved to the bottom it sits behind the - // system navigation bar (tabs unclickable). Pad it by the nav-bar inset; the pager is laid - // out relative to the tab strip so it follows automatically. requestApplyInsets() in - // updateTabLayoutPosition() re-runs this when the user toggles the position. - ViewCompat.setOnApplyWindowInsetsListener(binding.mainTabLayout, (v, insets) -> { - final int navBottom = mainTabsPositionBottom - ? insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom : 0; - v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(), navBottom); - return insets; - }); - setupTabs(); updateTabLayoutPosition(); } @@ -248,9 +235,6 @@ private void updateTabLayoutPosition() { tabLayout.setTabRippleColor(ColorStateList.valueOf(iconColor).withAlpha(32)); tabLayout.setTabIconTint(ColorStateList.valueOf(iconColor)); tabLayout.setSelectedTabIndicatorColor(iconColor); - - // Re-apply the nav-bar inset padding for the new position (added at bottom, cleared at top). - ViewCompat.requestApplyInsets(tabLayout); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/util/WindowInsetsHelper.java b/app/src/main/java/org/schabi/newpipe/util/WindowInsetsHelper.java index 18f656b0b0..c77446f437 100644 --- a/app/src/main/java/org/schabi/newpipe/util/WindowInsetsHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/WindowInsetsHelper.java @@ -71,6 +71,24 @@ public static void applyStatusBarInsets(@NonNull final AppCompatActivity activit }); } + /** + * Pads the bottom of a content view by the navigation-bar inset, so its last items stay above + * the system navigation bar in edge-to-edge (targetSdk 35+). Used on the main fragment holder + * so feed/search/channel/playlist lists are not drawn behind the nav bar. + */ + public static void applyNavigationBarInsets(@NonNull final View content) { + final int initialLeft = content.getPaddingLeft(); + final int initialTop = content.getPaddingTop(); + final int initialRight = content.getPaddingRight(); + final int initialBottom = content.getPaddingBottom(); + + ViewCompat.setOnApplyWindowInsetsListener(content, (view, insets) -> { + final Insets navBars = insets.getInsets(WindowInsetsCompat.Type.navigationBars()); + view.setPadding(initialLeft, initialTop, initialRight, initialBottom + navBars.bottom); + return insets; + }); + } + private static int getActionBarSize(@NonNull final AppCompatActivity activity) { final TypedValue typedValue = new TypedValue(); if (activity.getTheme().resolveAttribute(android.R.attr.actionBarSize, typedValue, true)) { From 8e0560827a5c3dbc7db1dbab3466e746407cf05a Mon Sep 17 00:00:00 2001 From: Priveetee Date: Wed, 17 Jun 2026 14:53:05 +0200 Subject: [PATCH 3/3] fix(ui): keep secondary screens (settings, downloads, about, error, captcha, login) above the nav bar --- app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt | 2 ++ app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt | 3 +++ .../java/org/schabi/newpipe/download/DownloadActivity.java | 2 ++ app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java | 2 ++ .../main/java/org/schabi/newpipe/error/ReCaptchaActivity.java | 2 ++ .../org/schabi/newpipe/settings/BaseFilterListActivity.java | 2 ++ .../java/org/schabi/newpipe/settings/SettingsActivity.java | 3 +++ .../org/schabi/newpipe/views/BaseLoginWebViewActivity.java | 3 +++ 8 files changed, 19 insertions(+) diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt index 4abab85702..76f18d2ed3 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt @@ -79,6 +79,8 @@ class AboutActivity : AppCompatActivity() { aboutAppVersion.text = BuildConfig.VERSION_NAME aboutGithubLink.openLink(R.string.github_url) aboutDonationLink.openLink(R.string.donation_url) + // Edge-to-edge (targetSdk 35+): keep the bottom of the page above the nav bar. + WindowInsetsHelper.applyNavigationBarInsets(root) return root } } diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt index c816d78be5..e1820c9a66 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt @@ -11,6 +11,7 @@ import org.schabi.newpipe.R import org.schabi.newpipe.about.LicenseFragmentHelper.showLicense import org.schabi.newpipe.databinding.FragmentLicensesBinding import org.schabi.newpipe.databinding.ItemSoftwareComponentBinding +import org.schabi.newpipe.util.WindowInsetsHelper /** * Fragment containing the software licenses. @@ -67,6 +68,8 @@ class LicenseFragment : Fragment() { registerForContextMenu(root) } activeLicense?.let { compositeDisposable.add(showLicense(activity, it)) } + // Edge-to-edge (targetSdk 35+): keep the bottom of the licenses list above the nav bar. + WindowInsetsHelper.applyNavigationBarInsets(binding.root) return binding.root } diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java index 61bc320617..75dcc42bce 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java @@ -47,6 +47,8 @@ protected void onCreate(final Bundle savedInstanceState) { setSupportActionBar(downloaderBinding.toolbarLayout.toolbar); WindowInsetsHelper.applyStatusBarInsets(this, downloaderBinding.toolbarLayout.toolbar); + // Edge-to-edge (targetSdk 35+): keep the bottom of the downloads list above the nav bar. + WindowInsetsHelper.applyNavigationBarInsets(downloaderBinding.frame); final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java index db1e21769e..00eb373918 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java @@ -108,6 +108,8 @@ protected void onCreate(final Bundle savedInstanceState) { WindowInsetsHelper.applyStatusBarInsets(this, activityErrorBinding.toolbarLayout.toolbar, activityErrorBinding.scrollView); + // Edge-to-edge (targetSdk 35+): keep the bottom of a long error report above the nav bar. + WindowInsetsHelper.applyNavigationBarInsets(activityErrorBinding.scrollView); final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java index 753bcfdb53..490d09b890 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java +++ b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java @@ -81,6 +81,8 @@ protected void onCreate(final Bundle savedInstanceState) { WindowInsetsHelper.applyStatusBarInsets(this, recaptchaBinding.toolbar, recaptchaBinding.reCaptchaWebView); + // Edge-to-edge (targetSdk 35+): keep the captcha web view content above the nav bar. + WindowInsetsHelper.applyNavigationBarInsets(recaptchaBinding.reCaptchaWebView); final String url = sanitizeRecaptchaUrl(getIntent().getStringExtra(RECAPTCHA_URL_EXTRA)); // set return to Cancel by default diff --git a/app/src/main/java/org/schabi/newpipe/settings/BaseFilterListActivity.java b/app/src/main/java/org/schabi/newpipe/settings/BaseFilterListActivity.java index 5b08f6d58b..e77493a39b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BaseFilterListActivity.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BaseFilterListActivity.java @@ -63,6 +63,8 @@ protected void onCreate(Bundle savedInstanceState) { recyclerView = findViewById(R.id.filter_list); recyclerView.setLayoutManager(new LinearLayoutManager(this)); + // Edge-to-edge (targetSdk 35+): keep the bottom of the filter list above the nav bar. + WindowInsetsHelper.applyNavigationBarInsets(recyclerView); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); loadFilterItems(); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java index 65f72b66f8..09b77f444f 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java @@ -106,6 +106,9 @@ protected void onCreate(final Bundle savedInstanceBundle) { WindowInsetsHelper.applyStatusBarInsets(this, settingsLayoutBinding.settingsToolbarLayout.toolbar, settingsLayoutBinding.settingsFragmentHolder); + // Edge-to-edge (targetSdk 35+): keep the last preference entry on long, scrollable + // settings pages above the system navigation bar. + WindowInsetsHelper.applyNavigationBarInsets(settingsLayoutBinding.settingsFragmentHolder); if (restored) { // Restore state diff --git a/app/src/main/java/org/schabi/newpipe/views/BaseLoginWebViewActivity.java b/app/src/main/java/org/schabi/newpipe/views/BaseLoginWebViewActivity.java index b08afa4ebe..6ebb3ee245 100644 --- a/app/src/main/java/org/schabi/newpipe/views/BaseLoginWebViewActivity.java +++ b/app/src/main/java/org/schabi/newpipe/views/BaseLoginWebViewActivity.java @@ -7,6 +7,7 @@ import android.webkit.WebViewClient; import androidx.appcompat.app.AppCompatActivity; import org.schabi.newpipe.R; +import org.schabi.newpipe.util.WindowInsetsHelper; public abstract class BaseLoginWebViewActivity extends AppCompatActivity { @@ -18,6 +19,8 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.login_webview); webView = findViewById(R.id.login_webview); + // Edge-to-edge (targetSdk 35+): keep the bottom of the login page above the nav bar. + WindowInsetsHelper.applyNavigationBarInsets(webView); configureWebView(); webView.setWebViewClient(createWebViewClient()); loadLoginUrl();