From 7cec7f535982ca6ef7a19b209339c9157f4395e1 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 5 Mar 2025 16:56:16 +0200 Subject: [PATCH] Fix window insets handling --- .../kotatsu/core/ui/BasePreferenceFragment.kt | 15 +++++- .../core/ui/FragmentContainerActivity.kt | 17 +++++++ .../download/ui/list/DownloadsActivity.kt | 27 +++++++++-- .../koitharu/kotatsu/main/ui/MainActivity.kt | 8 ++++ .../kotatsu/settings/SettingsActivity.kt | 29 +++++++++-- .../settings/search/SettingsSearchFragment.kt | 3 ++ .../sources/catalog/SourcesCatalogActivity.kt | 31 ++++++++++-- .../kotatsu/stats/ui/StatsActivity.kt | 48 ++++++++++++++++++- .../res/layout-w600dp-land/activity_main.xml | 6 ++- .../layout-w600dp-land/activity_settings.xml | 2 +- .../res/layout-w600dp-land/activity_stats.xml | 6 ++- .../main/res/layout/activity_container.xml | 2 +- app/src/main/res/layout/activity_main.xml | 3 +- app/src/main/res/layout/activity_settings.xml | 2 +- app/src/main/res/layout/activity_stats.xml | 2 +- 15 files changed, 181 insertions(+), 20 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt index 03c54ebd5..cc25dbf5f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/BasePreferenceFragment.kt @@ -5,6 +5,8 @@ import android.os.Bundle import android.view.Gravity import android.view.View import androidx.annotation.StringRes +import androidx.core.view.ancestors +import androidx.fragment.app.FragmentContainerView import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen @@ -12,6 +14,7 @@ import androidx.preference.get import androidx.recyclerview.widget.RecyclerView import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.EntryPointAccessors +import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner @@ -49,7 +52,12 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : val themedContext = (view.parentView ?: view).context view.setBackgroundColor(themedContext.getThemeColor(android.R.attr.colorBackground)) listView.clipToPadding = false - listView.consumeInsetsAsPadding(Gravity.BOTTOM or Gravity.START or Gravity.END) + val insetsEdges = Gravity.BOTTOM or when { + !resources.getBoolean(R.bool.is_tablet) -> Gravity.FILL_HORIZONTAL + isMaster() -> Gravity.START + else -> Gravity.END + } + listView.consumeInsetsAsPadding(insetsEdges) } override fun onResume() { @@ -91,4 +99,9 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : } return -1 } + + private fun isMaster(): Boolean { + val container = view?.ancestors?.firstNotNullOfOrNull { it as? FragmentContainerView } ?: return false + return container.id == R.id.container_master + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/FragmentContainerActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/FragmentContainerActivity.kt index 5ef60515f..8a0d7c646 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/FragmentContainerActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/FragmentContainerActivity.kt @@ -1,7 +1,12 @@ package org.koitharu.kotatsu.core.ui import android.os.Bundle +import android.view.View import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.OnApplyWindowInsetsListener +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.commit import com.google.android.material.appbar.AppBarLayout @@ -14,6 +19,7 @@ import org.koitharu.kotatsu.main.ui.owners.SnackbarOwner @AndroidEntryPoint abstract class FragmentContainerActivity(private val fragmentClass: Class) : BaseActivity(), + OnApplyWindowInsetsListener, AppBarOwner, SnackbarOwner { @@ -27,6 +33,7 @@ abstract class FragmentContainerActivity(private val fragmentClass: Class(), DownloadItemListener, + OnApplyWindowInsetsListener, ListSelectionController.Callback { @Inject @@ -43,6 +47,7 @@ class DownloadsActivity : BaseActivity(), super.onCreate(savedInstanceState) setContentView(ActivityDownloadsBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) + ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root, this) val downloadsAdapter = DownloadsAdapter(this, coil, this) val decoration = TypedListSpacingDecoration(this, false) selectionController = ListSelectionController( @@ -52,7 +57,6 @@ class DownloadsActivity : BaseActivity(), callback = this, ) with(viewBinding.recyclerView) { - consumeInsetsAsPadding(Gravity.START or Gravity.END or Gravity.BOTTOM) setHasFixedSize(true) addItemDecoration(decoration) adapter = downloadsAdapter @@ -68,6 +72,23 @@ class DownloadsActivity : BaseActivity(), viewModel.hasCancellableWorks.observe(this, menuInvalidator) } + override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat { + val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + viewBinding.recyclerView.updatePadding( + left = bars.left, + right = bars.right, + bottom = bars.bottom, + ) + viewBinding.appbar.updatePadding( + left = bars.left, + right = bars.right, + top = bars.top, + ) + return return WindowInsetsCompat.Builder(insets) + .setInsets(WindowInsetsCompat.Type.systemBars(), Insets.NONE) + .build() + } + override fun onItemClick(item: DownloadItemModel, view: View) { if (selectionController.onItemClick(item.id.mostSignificantBits)) { return diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt index e43dc5d26..0d620bb41 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainActivity.kt @@ -25,6 +25,7 @@ import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding +import androidx.core.view.updatePaddingRelative import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.commit @@ -224,9 +225,16 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav barsInsets.start(v) } } + viewBinding.bottomNav?.updatePadding( + left = barsInsets.left, + right = barsInsets.right, + bottom = barsInsets.bottom, + ) return viewBinding.navRail?.let { navRail -> navRail.updateLayoutParams { marginStart = barsInsets.start(v) + topMargin = barsInsets.top + bottomMargin = barsInsets.bottom } WindowInsetsCompat.Builder(insets) .setInsets(WindowInsetsCompat.Type.systemBars(), barsInsets.consumeRelative(v, start = true)) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt index 82708e7b5..62a9225d1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -2,8 +2,15 @@ package org.koitharu.kotatsu.settings import android.content.Intent import android.os.Bundle +import android.view.View +import android.view.ViewGroup import androidx.activity.viewModels +import androidx.core.view.OnApplyWindowInsetsListener +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.core.view.updatePaddingRelative import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentTransaction @@ -17,8 +24,10 @@ import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.nav.AppRouter import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.ext.buildBundle +import org.koitharu.kotatsu.core.util.ext.end import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent +import org.koitharu.kotatsu.core.util.ext.start import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ActivitySettingsBinding import org.koitharu.kotatsu.main.ui.owners.AppBarOwner @@ -36,6 +45,7 @@ import org.koitharu.kotatsu.settings.userdata.UserDataSettingsFragment @AndroidEntryPoint class SettingsActivity : BaseActivity(), + OnApplyWindowInsetsListener, PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, AppBarOwner { @@ -45,14 +55,12 @@ class SettingsActivity : private val isMasterDetails get() = viewBinding.containerMaster != null - private var screenPadding = 0 - private val viewModel: SettingsSearchViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivitySettingsBinding.inflate(layoutInflater)) - screenPadding = resources.getDimensionPixelOffset(R.dimen.screen_padding) + ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root, this) supportActionBar?.setDisplayHomeAsUpEnabled(true) val fm = supportFragmentManager val currentFragment = fm.findFragmentById(R.id.container) @@ -69,6 +77,21 @@ class SettingsActivity : viewModel.onNavigateToPreference.observeEvent(this, ::navigateToPreference) } + override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat { + val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + val isTablet = viewBinding.containerMaster != null + viewBinding.appbar.updatePaddingRelative( + start = bars.start(v), + top = bars.top, + end = if (isTablet) 0 else bars.end(v), + ) + viewBinding.textViewHeader?.updateLayoutParams { + marginEnd = bars.end(v) + topMargin = bars.top + } + return insets + } + override fun onPreferenceStartFragment( caller: PreferenceFragmentCompat, pref: Preference, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt index 20db9ee64..98e0c49f9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/search/SettingsSearchFragment.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.settings.search import android.os.Bundle +import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,6 +12,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.databinding.FragmentSearchSuggestionBinding import org.koitharu.kotatsu.list.ui.adapter.ListItemType @@ -28,6 +30,7 @@ class SettingsSearchFragment : BaseFragment(), override fun onViewBindingCreated(binding: FragmentSearchSuggestionBinding, savedInstanceState: Bundle?) { super.onViewBindingCreated(binding, savedInstanceState) + binding.root.consumeInsetsAsPadding(Gravity.FILL_HORIZONTAL or Gravity.BOTTOM) val adapter = BaseListAdapter() .addDelegate(ListItemType.NAV_ITEM, settingsItemAD(this)) adapter.addListListener(this) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt index d8c511340..5caec0235 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt @@ -1,13 +1,17 @@ package org.koitharu.kotatsu.settings.sources.catalog import android.os.Bundle -import android.view.Gravity import android.view.Menu import android.view.MenuItem import android.view.View import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.core.graphics.Insets +import androidx.core.view.OnApplyWindowInsetsListener +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import coil3.ImageLoader import com.google.android.material.appbar.AppBarLayout import com.google.android.material.chip.Chip @@ -23,7 +27,6 @@ import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.ui.widgets.ChipsView.ChipModel import org.koitharu.kotatsu.core.util.LocaleComparator -import org.koitharu.kotatsu.core.util.ext.consumeInsetsAsPadding import org.koitharu.kotatsu.core.util.ext.getDisplayName import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent @@ -37,7 +40,10 @@ import javax.inject.Inject @AndroidEntryPoint class SourcesCatalogActivity : BaseActivity(), OnListItemClickListener, - AppBarOwner, MenuItem.OnActionExpandListener, ChipsView.OnChipClickListener { + OnApplyWindowInsetsListener, + AppBarOwner, + MenuItem.OnActionExpandListener, + ChipsView.OnChipClickListener { @Inject lateinit var coil: ImageLoader @@ -51,9 +57,9 @@ class SourcesCatalogActivity : BaseActivity(), super.onCreate(savedInstanceState) setContentView(ActivitySourcesCatalogBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) + ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root, this) val sourcesAdapter = SourcesCatalogAdapter(this, coil, this) with(viewBinding.recyclerView) { - consumeInsetsAsPadding(Gravity.START or Gravity.END or Gravity.BOTTOM) setHasFixedSize(true) addItemDecoration(TypedListSpacingDecoration(context, false)) adapter = sourcesAdapter @@ -71,6 +77,23 @@ class SourcesCatalogActivity : BaseActivity(), addMenuProvider(SourcesCatalogMenuProvider(this, viewModel, this)) } + override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat { + val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + viewBinding.recyclerView.updatePadding( + left = bars.left, + right = bars.right, + bottom = bars.bottom, + ) + viewBinding.appbar.updatePadding( + left = bars.left, + right = bars.right, + top = bars.top, + ) + return return WindowInsetsCompat.Builder(insets) + .setInsets(WindowInsetsCompat.Type.systemBars(), Insets.NONE) + .build() + } + override fun onChipClick(chip: Chip, data: Any?) { when (data) { is ContentType -> viewModel.setContentType(data, !chip.isChecked) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt index 3add5e86e..1d792a901 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt @@ -4,12 +4,19 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.View +import android.view.ViewGroup import android.view.ViewStub import android.widget.CompoundButton import androidx.activity.viewModels import androidx.appcompat.widget.PopupMenu +import androidx.core.graphics.Insets +import androidx.core.view.OnApplyWindowInsetsListener +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.isGone import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.core.view.updatePaddingRelative import androidx.recyclerview.widget.AsyncListDiffer import coil3.ImageLoader import com.google.android.material.chip.Chip @@ -24,12 +31,14 @@ import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver import org.koitharu.kotatsu.core.util.KotatsuColors +import org.koitharu.kotatsu.core.util.ext.end import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.setTextAndVisible import org.koitharu.kotatsu.core.util.ext.showOrHide +import org.koitharu.kotatsu.core.util.ext.start import org.koitharu.kotatsu.databinding.ActivityStatsBinding import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding import org.koitharu.kotatsu.list.ui.adapter.ListItemType @@ -41,10 +50,13 @@ import javax.inject.Inject @AndroidEntryPoint class StatsActivity : BaseActivity(), + OnApplyWindowInsetsListener, OnListItemClickListener, PieChartView.OnSegmentClickListener, AsyncListDiffer.ListListener, - ViewStub.OnInflateListener, View.OnClickListener, CompoundButton.OnCheckedChangeListener { + ViewStub.OnInflateListener, + View.OnClickListener, + CompoundButton.OnCheckedChangeListener { @Inject lateinit var coil: ImageLoader @@ -55,6 +67,7 @@ class StatsActivity : BaseActivity(), super.onCreate(savedInstanceState) setContentView(ActivityStatsBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) + ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root, this) val adapter = BaseListAdapter() .addDelegate(ListItemType.FEED, statsAD(this)) .addListListener(this) @@ -88,6 +101,39 @@ class StatsActivity : BaseActivity(), } } + override fun onApplyWindowInsets( + v: View, + insets: WindowInsetsCompat + ): WindowInsetsCompat { + val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + val isTablet = viewBinding.guidelineCenter != null + viewBinding.appbar.updatePaddingRelative( + start = bars.start(v), + top = bars.top, + end = if (isTablet) 0 else bars.end(v), + ) + val badgePadding = resources.getDimensionPixelOffset(R.dimen.list_spacing_large) + viewBinding.scrollViewChips.updatePaddingRelative( + start = badgePadding + if (isTablet) 0 else bars.start(v), + end = badgePadding + bars.end(v), + top = if (isTablet) bars.top else 0, + ) + viewBinding.recyclerView.updatePaddingRelative( + start = if (isTablet) 0 else bars.start(v), + end = bars.end(v), + bottom = bars.bottom, + ) + viewBinding.chart.updateLayoutParams { + val baseMargin = topMargin + bottomMargin = if (isTablet) baseMargin + bars.bottom else baseMargin + marginStart = baseMargin + bars.start(v) + marginEnd = if (isTablet) baseMargin else baseMargin + bars.end(v) + } + return return WindowInsetsCompat.Builder(insets) + .setInsets(WindowInsetsCompat.Type.systemBars(), Insets.NONE) + .build() + } + override fun onClick(v: View) { when (v.id) { R.id.chip_period -> showPeriodSelector() diff --git a/app/src/main/res/layout-w600dp-land/activity_main.xml b/app/src/main/res/layout-w600dp-land/activity_main.xml index fe3b4655b..1b6d10d92 100644 --- a/app/src/main/res/layout-w600dp-land/activity_main.xml +++ b/app/src/main/res/layout-w600dp-land/activity_main.xml @@ -12,10 +12,14 @@ android:id="@+id/navRail" android:layout_width="wrap_content" android:layout_height="match_parent" + android:fitsSystemWindows="false" app:elevation="1dp" app:headerLayout="@layout/navigation_rail_fab" app:labelVisibilityMode="labeled" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:paddingBottomSystemWindowInsets="false" + app:paddingStartSystemWindowInsets="false" + app:paddingTopSystemWindowInsets="false" /> @@ -40,7 +41,8 @@ + android:layout_height="wrap_content" + android:layout_gravity="center_vertical"> + android:fitsSystemWindows="false"> + android:layout_gravity="bottom" + android:fitsSystemWindows="false" /> diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index cb1fa645d..a730936af 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -9,7 +9,7 @@ android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true" + android:fitsSystemWindows="false" app:liftOnScroll="false">