|
@@ -1,158 +0,0 @@
|
|
|
-/*
|
|
|
- * Copyright 2021 The Android Open Source Project
|
|
|
- *
|
|
|
- * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
- * you may not use this file except in compliance with the License.
|
|
|
- * You may obtain a copy of the License at
|
|
|
- *
|
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
- *
|
|
|
- * Unless required by applicable law or agreed to in writing, software
|
|
|
- * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
- * See the License for the specific language governing permissions and
|
|
|
- * limitations under the License.
|
|
|
- */
|
|
|
-
|
|
|
-package eu.kanade.presentation.util
|
|
|
-
|
|
|
-import androidx.compose.animation.core.AnimationState
|
|
|
-import androidx.compose.animation.core.DecayAnimationSpec
|
|
|
-import androidx.compose.animation.core.animateDecay
|
|
|
-import androidx.compose.material3.TopAppBarScrollBehavior
|
|
|
-import androidx.compose.material3.TopAppBarScrollState
|
|
|
-import androidx.compose.ui.geometry.Offset
|
|
|
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
|
|
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
|
|
-import androidx.compose.ui.unit.Velocity
|
|
|
-import kotlin.math.abs
|
|
|
-
|
|
|
-/**
|
|
|
- * A [TopAppBarScrollBehavior] that adjusts its properties to affect the colors and height of a top
|
|
|
- * app bar.
|
|
|
- *
|
|
|
- * A top app bar that is set up with this [TopAppBarScrollBehavior] will immediately collapse when
|
|
|
- * the nested content is pulled up, and will expand back the collapsed area when the content is
|
|
|
- * pulled all the way down.
|
|
|
- *
|
|
|
- * @param decayAnimationSpec a [DecayAnimationSpec] that will be used by the top app bar motion
|
|
|
- * when the user flings the content. Preferably, this should match the animation spec used by the
|
|
|
- * scrollable content. See also [androidx.compose.animation.rememberSplineBasedDecay] for a
|
|
|
- * default [DecayAnimationSpec] that can be used with this behavior.
|
|
|
- * @param canScroll a callback used to determine whether scroll events are to be
|
|
|
- * handled by this [ExitUntilCollapsedScrollBehavior]
|
|
|
- */
|
|
|
-class ExitUntilCollapsedScrollBehavior(
|
|
|
- override val state: TopAppBarScrollState,
|
|
|
- val decayAnimationSpec: DecayAnimationSpec<Float>,
|
|
|
- val canScroll: () -> Boolean = { true },
|
|
|
-) : TopAppBarScrollBehavior {
|
|
|
- override val scrollFraction: Float
|
|
|
- get() = if (state.offsetLimit != 0f) state.offset / state.offsetLimit else 0f
|
|
|
- override var nestedScrollConnection =
|
|
|
- object : NestedScrollConnection {
|
|
|
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
|
|
- // Don't intercept if scrolling down.
|
|
|
- if (!canScroll() || available.y > 0f) return Offset.Zero
|
|
|
-
|
|
|
- val newOffset = (state.offset + available.y)
|
|
|
- val coerced =
|
|
|
- newOffset.coerceIn(minimumValue = state.offsetLimit, maximumValue = 0f)
|
|
|
- return if (newOffset == coerced) {
|
|
|
- // Nothing coerced, meaning we're in the middle of top app bar collapse or
|
|
|
- // expand.
|
|
|
- state.offset = coerced
|
|
|
- // Consume only the scroll on the Y axis.
|
|
|
- available.copy(x = 0f)
|
|
|
- } else {
|
|
|
- Offset.Zero
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPostScroll(
|
|
|
- consumed: Offset,
|
|
|
- available: Offset,
|
|
|
- source: NestedScrollSource,
|
|
|
- ): Offset {
|
|
|
- if (!canScroll()) return Offset.Zero
|
|
|
- state.contentOffset += consumed.y
|
|
|
-
|
|
|
- if (available.y < 0f || consumed.y < 0f) {
|
|
|
- // When scrolling up, just update the state's offset.
|
|
|
- val oldOffset = state.offset
|
|
|
- state.offset = (state.offset + consumed.y).coerceIn(
|
|
|
- minimumValue = state.offsetLimit,
|
|
|
- maximumValue = 0f,
|
|
|
- )
|
|
|
- return Offset(0f, state.offset - oldOffset)
|
|
|
- }
|
|
|
-
|
|
|
- if (consumed.y == 0f && available.y > 0) {
|
|
|
- // Reset the total offset to zero when scrolling all the way down. This will
|
|
|
- // eliminate some float precision inaccuracies.
|
|
|
- state.contentOffset = 0f
|
|
|
- }
|
|
|
-
|
|
|
- if (available.y > 0f) {
|
|
|
- // Adjust the offset in case the consumed delta Y is less than what was recorded
|
|
|
- // as available delta Y in the pre-scroll.
|
|
|
- val oldOffset = state.offset
|
|
|
- state.offset = (state.offset + available.y).coerceIn(
|
|
|
- minimumValue = state.offsetLimit,
|
|
|
- maximumValue = 0f,
|
|
|
- )
|
|
|
- return Offset(0f, state.offset - oldOffset)
|
|
|
- }
|
|
|
- return Offset.Zero
|
|
|
- }
|
|
|
-
|
|
|
- override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
|
|
|
- val result = super.onPostFling(consumed, available)
|
|
|
- if ((available.y < 0f && state.contentOffset == 0f) ||
|
|
|
- (available.y > 0f && state.offset < 0f)
|
|
|
- ) {
|
|
|
- return result +
|
|
|
- onTopBarFling(
|
|
|
- scrollBehavior = this@ExitUntilCollapsedScrollBehavior,
|
|
|
- initialVelocity = available.y,
|
|
|
- decayAnimationSpec = decayAnimationSpec,
|
|
|
- )
|
|
|
- }
|
|
|
- return result
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Tachiyomi: Remove snap behavior
|
|
|
- */
|
|
|
-private suspend fun onTopBarFling(
|
|
|
- scrollBehavior: TopAppBarScrollBehavior,
|
|
|
- initialVelocity: Float,
|
|
|
- decayAnimationSpec: DecayAnimationSpec<Float>,
|
|
|
-): Velocity {
|
|
|
- if (abs(initialVelocity) > 1f) {
|
|
|
- var remainingVelocity = initialVelocity
|
|
|
- var lastValue = 0f
|
|
|
- AnimationState(
|
|
|
- initialValue = 0f,
|
|
|
- initialVelocity = initialVelocity,
|
|
|
- )
|
|
|
- .animateDecay(decayAnimationSpec) {
|
|
|
- val delta = value - lastValue
|
|
|
- val initialOffset = scrollBehavior.state.offset
|
|
|
- scrollBehavior.state.offset =
|
|
|
- (initialOffset + delta).coerceIn(
|
|
|
- minimumValue = scrollBehavior.state.offsetLimit,
|
|
|
- maximumValue = 0f,
|
|
|
- )
|
|
|
- val consumed = abs(initialOffset - scrollBehavior.state.offset)
|
|
|
- lastValue = value
|
|
|
- remainingVelocity = this.velocity
|
|
|
- // avoid rounding errors and stop if anything is unconsumed
|
|
|
- if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
|
|
|
- }
|
|
|
- return Velocity(0f, remainingVelocity)
|
|
|
- }
|
|
|
- return Velocity.Zero
|
|
|
-}
|