diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 0586fed66..3894c1fab 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/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt index 4abab8570..76f18d2ed 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 c816d78be..e1820c9a6 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 61bc32061..75dcc42bc 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 db1e21769..00eb37391 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 753bcfdb5..490d09b89 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/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 5f10b4182..e37086888 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/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 10ed4fb44..9baf6cffb 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/java/org/schabi/newpipe/settings/BaseFilterListActivity.java b/app/src/main/java/org/schabi/newpipe/settings/BaseFilterListActivity.java index 5b08f6d58..e77493a39 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 65f72b66f..09b77f444 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/util/WindowInsetsHelper.java b/app/src/main/java/org/schabi/newpipe/util/WindowInsetsHelper.java index 18f656b0b..c77446f43 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)) { 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 b08afa4eb..6ebb3ee24 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(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 97ccd199e..1d6072700 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" />