diff --git a/app/src/main/kotlin/androidx/compose/material3/NavigationDrawer2.kt b/app/src/main/kotlin/androidx/compose/material3/NavigationDrawer2.kt index 0291b4d52..5348d91f9 100644 --- a/app/src/main/kotlin/androidx/compose/material3/NavigationDrawer2.kt +++ b/app/src/main/kotlin/androidx/compose/material3/NavigationDrawer2.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.positionChange @@ -42,6 +43,8 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.lerp +import com.hippo.ehviewer.ui.tools.animateFloatMergeOneWayPredictiveBackAsState import kotlin.math.roundToInt import kotlinx.coroutines.CancellationException import kotlinx.coroutines.launch @@ -306,6 +309,16 @@ fun ModalNavigationDrawer( }, color = scrimColor, ) + val predictiveState by animateFloatMergeOneWayPredictiveBackAsState(drawerState.isOpen) { + drawerState.close() + } + val predictiveModifier = if (drawerState.isOpen) { + Modifier.scale(lerp(1f, 0.9f, predictiveState)).offset { + IntOffset(lerp(0f, minValue * 0.05f, predictiveState).roundToInt(), 0) + } + } else { + Modifier + } Box( modifier = Modifier.offset { IntOffset(drawerState.requireOffset().roundToInt(), 0) @@ -314,7 +327,7 @@ fun ModalNavigationDrawer( if (w != 0) { minValue = -it.size.width.toFloat() } - }, + } then predictiveModifier, ) { drawerContent() } @@ -398,6 +411,16 @@ fun SideDrawer( }, color = scrimColor, ) + val predictiveState by animateFloatMergeOneWayPredictiveBackAsState(drawerState.isOpen) { + drawerState.close() + } + val predictiveModifier = if (drawerState.isOpen) { + Modifier.scale(lerp(1f, 0.9f, predictiveState)).offset { + IntOffset(lerp(0f, minValue * 0.05f, predictiveState).roundToInt(), 0) + } + } else { + Modifier + } Box( modifier = Modifier.offset { IntOffset(drawerState.requireOffset().roundToInt(), 0) @@ -406,7 +429,7 @@ fun SideDrawer( if (w != 0) { minValue = it.size.width.toFloat() } - }.align(Alignment.TopEnd), + }.align(Alignment.TopEnd) then predictiveModifier, ) { drawerContent() } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt index c524b09db..e71987e48 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt @@ -26,7 +26,6 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.widget.Toast -import androidx.activity.compose.BackHandler import androidx.annotation.RequiresApi import androidx.annotation.StringRes import androidx.compose.foundation.Image @@ -300,9 +299,6 @@ class MainActivity : EhActivity() { recomposeScope.invalidate() } } - BackHandler(drawerState.isOpen) { - closeDrawer() - } LocalTouchSlopProvider(Settings.touchSlopFactor.toFloat()) { ModalNavigationDrawer( drawerContent = { @@ -355,11 +351,6 @@ class MainActivity : EhActivity() { sideDrawerState.open() } } - BackHandler(sideDrawerState.isOpen) { - scope.launch { - sideDrawerState.close() - } - } SideDrawer( drawerContent = { if (sheet != null) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/PredictiveBack.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/PredictiveBack.kt index 93ebf3214..80b15b913 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/PredictiveBack.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/PredictiveBack.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState +import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import kotlinx.coroutines.CancellationException import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch @@ -72,3 +73,31 @@ fun animateFloatMergePredictiveBackAsState( } return animatable.asState() } + +@Composable +fun animateFloatMergeOneWayPredictiveBackAsState( + enable: Boolean, + animationSpec: AnimationSpec = spring(), + predictiveBackInterpolator: Easing = EaseOut, + onBack: suspend () -> Unit, +): State { + val animatable = remember { Animatable(0f, Float.VectorConverter) } + PredictiveBackHandler(enable) { progress -> + try { + progress.collect { + val transformed = predictiveBackInterpolator.transform(it.progress) + animatable.snapTo(transformed) + } + } catch (e: CancellationException) { + withNonCancellableContext { + animatable.animateTo(0f, animationSpec) + } + throw e + } + withNonCancellableContext { + onBack() + animatable.snapTo(0f) + } + } + return animatable.asState() +}