Optional Predictive NavAnim

This commit is contained in:
GG Bond authored and Vladimir Dostoyevskiy committed 2023-11-27 13:56:36 +08:00
1 parent 4194087f0b
commit 2611601fb9
5 files changed
+59 -40

No files matched your search

@@ -17,6 +17,7 @@
package androidx.navigation.compose
import android.annotation.SuppressLint
import androidx.activity.compose.BackHandler
import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
@@ -53,6 +54,8 @@ import androidx.navigation.NavHostController
import androidx.navigation.Navigator
import androidx.navigation.createGraph
import androidx.navigation.get
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.collectAsState
import kotlin.math.roundToLong
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.NonCancellable
@@ -280,54 +283,63 @@ public fun NavHost(
}
}
val duration = 500000000L
val transitionState = remember { MutableTransitionState(backStackEntry) }
val transition = updateTransition(transitionState = transitionState, label = "entry")
val animatedFraction = remember { Animatable(0f).also { it.updateBounds(lowerBound = 0f, upperBound = 1f) } }
val enablePredictiveBack by Settings.predictiveNavAnim.collectAsState()
val transition = if (enablePredictiveBack) {
val duration = 500000000L
val transitionState = remember { MutableTransitionState(backStackEntry) }
val transition = updateTransition(transitionState = transitionState, label = "entry")
val animatedFraction = remember { Animatable(0f).also { it.updateBounds(lowerBound = 0f, upperBound = 1f) } }
fun seekToFraction(lState: NavBackStackEntry, rState: NavBackStackEntry) {
val fraction = animatedFraction.value
val playTimeNanos = (fraction * duration).roundToLong()
transition.setPlaytimeAfterInitialAndTargetStateEstablished(lState, rState, playTimeNanos)
}
fun seekToFraction(lState: NavBackStackEntry, rState: NavBackStackEntry) {
val fraction = animatedFraction.value
val playTimeNanos = (fraction * duration).roundToLong()
transition.setPlaytimeAfterInitialAndTargetStateEstablished(lState, rState, playTimeNanos)
}
LaunchedEffect(backStackEntry) {
try {
animatedFraction.animateTo(1f, animationSpec = tween(500, easing = LinearEasing)) {
seekToFraction(transitionState.currentState, backStackEntry)
}
} finally {
withContext(NonCancellable) {
animatedFraction.snapTo(0f)
transition.setPlaytimeAfterInitialAndTargetStateEstablished(
backStackEntry,
backStackEntry,
0,
)
LaunchedEffect(backStackEntry) {
try {
animatedFraction.animateTo(1f, animationSpec = tween(500, easing = LinearEasing)) {
seekToFraction(transitionState.currentState, backStackEntry)
}
} finally {
withContext(NonCancellable) {
animatedFraction.snapTo(0f)
transition.setPlaytimeAfterInitialAndTargetStateEstablished(
backStackEntry,
backStackEntry,
0,
)
}
}
}
}
PredictiveBackHandler(currentBackStack.size > 1) { flow ->
// Prev can never be null, i.e. BackHandler should not enabled when prev is null
val prev = currentBackStack.last { it != backStackEntry }
try {
flow.collect {
val interpolated = EaseOut.transform(it.progress)
animatedFraction.snapTo(interpolated)
seekToFraction(backStackEntry, prev)
PredictiveBackHandler(currentBackStack.size > 1) { flow ->
// Prev can never be null, i.e. BackHandler should not enabled when prev is null
val prev = currentBackStack.last { it != backStackEntry }
try {
flow.collect {
val interpolated = EaseOut.transform(it.progress)
animatedFraction.snapTo(interpolated)
seekToFraction(backStackEntry, prev)
}
navController.popBackStack()
} catch (e: CancellationException) {
withContext(NonCancellable) {
animatedFraction.snapTo(0f)
transition.setPlaytimeAfterInitialAndTargetStateEstablished(
backStackEntry,
backStackEntry,
0,
)
}
}
}
transition
} else {
BackHandler(currentBackStack.size > 1) {
navController.popBackStack()
} catch (e: CancellationException) {
withContext(NonCancellable) {
animatedFraction.snapTo(0f)
transition.setPlaytimeAfterInitialAndTargetStateEstablished(
backStackEntry,
backStackEntry,
0,
)
}
}
updateTransition(backStackEntry, label = "entry")
}
transition.AnimatedContent(
@@ -106,6 +106,7 @@ object Settings : DataStorePreferences(null) {
val thumbColumns = intPref("thumb_columns", 3)
val listThumbSize = intPref("list_tile_size", 40)
val meteredNetworkWarning = boolPref("cellular_network_warning", false)
val predictiveNavAnim = boolPref("predictive_nav_anim", true)
val needSignInFlow: Flow<Boolean>
var downloadScheme by stringOrNullPref("image_scheme", null)
@@ -141,6 +141,10 @@ fun AdvancedScreen(navigator: DestinationsNavigator) {
title = stringResource(id = R.string.preload_thumb_aggressively),
value = Settings::preloadThumbAggressively,
)
SwitchPreference(
title = stringResource(id = R.string.predictive_nav_anim),
value = Settings.predictiveNavAnim::value,
)
IntSliderPreference(
maxValue = 5,
minValue = 1,
@@ -524,6 +524,7 @@
<string name="list_tile_thumb_size">详情模式下缩略图大小</string>
<string name="account_name">账户</string>
<string name="preload_thumb_aggressively">激进地预载缩略图</string>
<string name="predictive_nav_anim">启用预见式返回导航动画</string>
<string name="auto_updates">自动检查更新</string>
<string name="update_frequency_never">从不</string>
<string name="update_frequency_daily">每天</string>
+1
View File
@@ -587,6 +587,7 @@
<string name="list_tile_thumb_size">Thumb size in thumb mode</string>
<string name="account_name">Account</string>
<string name="preload_thumb_aggressively">Preload thumbs aggressively</string>
<string name="predictive_nav_anim">Enable predictive back navigation animation</string>
<string name="user_agent">User Agent</string>
<string name="auto_updates">Automatically check for updates</string>
<string name="update_frequency_never">Never</string>