diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 3808d3a09..43bf12789 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -74,6 +74,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { val isNavLabelsVisible: Boolean get() = prefs.getBoolean(KEY_NAV_LABELS, true) + val isNavBarPinned: Boolean + get() = prefs.getBoolean(KEY_NAV_PINNED, false) + var gridSize: Int get() = prefs.getInt(KEY_GRID_SIZE, 100) set(value) = prefs.edit { putInt(KEY_GRID_SIZE, value) } @@ -653,6 +656,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_RELATED_MANGA = "related_manga" const val KEY_NAV_MAIN = "nav_main" const val KEY_NAV_LABELS = "nav_labels" + const val KEY_NAV_PINNED = "nav_pinned" const val KEY_32BIT_COLOR = "enhanced_colors" const val KEY_SOURCES_ORDER = "sources_sort_order" const val KEY_SOURCES_CATALOG = "sources_catalog" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/HideBottomNavigationOnScrollBehavior.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/HideBottomNavigationOnScrollBehavior.kt index 629ffcd12..0dbdd37f8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/HideBottomNavigationOnScrollBehavior.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/HideBottomNavigationOnScrollBehavior.kt @@ -25,6 +25,15 @@ class HideBottomNavigationOnScrollBehavior @JvmOverloads constructor( private var dyRatio = 1F + var isPinned: Boolean = false + set(value) { + field = value + if (value) { + offsetAnimator?.cancel() + offsetAnimator = null + } + } + override fun layoutDependsOn(parent: CoordinatorLayout, child: BottomNavigationView, dependency: View): Boolean { return dependency is AppBarLayout } @@ -51,7 +60,7 @@ class HideBottomNavigationOnScrollBehavior @JvmOverloads constructor( axes: Int, type: Int, ): Boolean { - if (axes != ViewCompat.SCROLL_AXIS_VERTICAL) { + if (isPinned || axes != ViewCompat.SCROLL_AXIS_VERTICAL) { return false } lastStartedType = type @@ -69,7 +78,9 @@ class HideBottomNavigationOnScrollBehavior @JvmOverloads constructor( type: Int, ) { super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type) - child.translationY = (child.translationY + (dy * dyRatio)).coerceIn(0F, child.height.toFloat()) + if (!isPinned) { + child.translationY = (child.translationY + (dy * dyRatio)).coerceIn(0F, child.height.toFloat()) + } } override fun onStopNestedScroll( @@ -78,7 +89,7 @@ class HideBottomNavigationOnScrollBehavior @JvmOverloads constructor( target: View, type: Int, ) { - if (lastStartedType == ViewCompat.TYPE_TOUCH || type == ViewCompat.TYPE_NON_TOUCH) { + if (!isPinned && (lastStartedType == ViewCompat.TYPE_TOUCH || type == ViewCompat.TYPE_NON_TOUCH)) { animateBottomNavigationVisibility(child, child.translationY < child.height / 2) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/SlidingBottomNavigationView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/SlidingBottomNavigationView.kt index 57e8fdcfa..bf4e260d7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/SlidingBottomNavigationView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/SlidingBottomNavigationView.kt @@ -38,6 +38,15 @@ class SlidingBottomNavigationView @JvmOverloads constructor( private var currentState = STATE_UP private var behavior = HideBottomNavigationOnScrollBehavior() + var isPinned: Boolean + get() = behavior.isPinned + set(value) { + behavior.isPinned = value + if (value) { + translationX = 0f + } + } + override fun getBehavior(): CoordinatorLayout.Behavior<*> { return behavior } 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 80492f6ad..7bffce988 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 @@ -13,6 +13,7 @@ import androidx.appcompat.view.ActionMode import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.graphics.Insets +import androidx.core.view.children import androidx.core.view.inputmethod.EditorInfoCompat import androidx.core.view.isInvisible import androidx.core.view.isVisible @@ -131,6 +132,7 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav viewModel.onFirstStart.observeEvent(this) { WelcomeSheet.show(supportFragmentManager) } + viewModel.isBottomNavPinned.observe(this, ::setNavbarPinned) searchSuggestionViewModel.isIncognitoModeEnabled.observe(this, this::onIncognitoModeChanged) } @@ -399,6 +401,22 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav } } + private fun setNavbarPinned(isPinned: Boolean) { + viewBinding.bottomNav?.isPinned = isPinned + for (view in viewBinding.appbar.children) { + val lp = view.layoutParams as? AppBarLayout.LayoutParams ?: continue + val scrollFlags = if (isPinned) { + lp.scrollFlags and SCROLL_FLAG_SCROLL.inv() + } else { + lp.scrollFlags or SCROLL_FLAG_SCROLL + } + if (scrollFlags != lp.scrollFlags) { + lp.scrollFlags = scrollFlags + view.layoutParams = lp + } + } + } + private inner class CloseSearchCallback : OnBackPressedCallback(false) { override fun handleOnBackPressed() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainViewModel.kt index aaaf655b3..88e3a7ed0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/MainViewModel.kt @@ -4,11 +4,13 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException import org.koitharu.kotatsu.core.github.AppUpdateRepository import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call @@ -46,6 +48,12 @@ class MainViewModel @Inject constructor( .withErrorHandling() .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, 0) + val isBottomNavPinned = settings.observeAsFlow( + AppSettings.KEY_NAV_PINNED, + ) { + isNavBarPinned + }.flowOn(Dispatchers.Default) + init { launchJob { appUpdateRepository.fetchUpdate() diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index 8b6253bc3..25f8f60db 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -19,7 +19,7 @@ android:orientation="vertical" android:paddingVertical="@dimen/list_spacing_normal" app:bubbleSize="small" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:layoutManager=".core.ui.list.FitHeightLinearLayoutManager" tools:listitem="@layout/item_feed" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c65040d1..c07b49f95 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -634,4 +634,6 @@ More frequently Frequency of check %1$s: %2$d + Pin navigation UI + Do not hide navgation bar and search view on scroll diff --git a/app/src/main/res/xml/pref_appearance.xml b/app/src/main/res/xml/pref_appearance.xml index 8e12a9ac2..1ab9e5660 100644 --- a/app/src/main/res/xml/pref_appearance.xml +++ b/app/src/main/res/xml/pref_appearance.xml @@ -76,6 +76,12 @@ android:key="nav_labels" android:title="@string/show_labels_in_navbar" /> + +