feat(ui): predictive back animation for Android 14+

This commit is contained in:
junkfood 2024-11-12 21:55:49 +08:00
parent c691a106d5
commit 2655cb5f43
No known key found for this signature in database
GPG Key ID: 2EA5B648DB112A34
3 changed files with 65 additions and 15 deletions

View File

@ -4,8 +4,10 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
@ -19,6 +21,7 @@
<application
android:name=".infrastructure.android.AndroidApp"
android:allowBackup="true"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/read_you"
android:roundIcon="@mipmap/ic_launcher_round"

View File

@ -8,38 +8,88 @@
package me.ash.reader.ui.ext
import android.os.Build
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDeepLink
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import me.ash.reader.ui.motion.EmphasizedAccelerate
import me.ash.reader.ui.motion.EmphasizedDecelerate
import me.ash.reader.ui.motion.materialSharedAxisXIn
import me.ash.reader.ui.motion.materialSharedAxisXOut
private const val INITIAL_OFFSET_FACTOR = 0.10f
private const val INITIAL_SCALE_FACTOR = 0.8f
fun NavGraphBuilder.animatedComposable(
route: String,
arguments: List<NamedNavArgument> = emptyList(),
deepLinks: List<NavDeepLink> = emptyList(),
content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
) = composable(
route: String,
arguments: List<NamedNavArgument> = emptyList(),
deepLinks: List<NavDeepLink> = emptyList(),
usePredictiveBack: Boolean = Build.VERSION.SDK_INT >= 34,
content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit,
) {
if (usePredictiveBack) {
animatedComposablePredictiveBack(route, arguments, deepLinks, content)
} else {
animatedComposableLegacy(route, arguments, deepLinks, content)
}
}
fun NavGraphBuilder.animatedComposablePredictiveBack(
route: String,
arguments: List<NamedNavArgument> = emptyList(),
deepLinks: List<NavDeepLink> = emptyList(),
content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit,
) =
composable(
route = route,
arguments = arguments,
deepLinks = deepLinks,
enterTransition = {
materialSharedAxisXIn(initialOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() })
},
enterTransition = { materialSharedAxisXIn(initialOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() }) },
exitTransition = {
materialSharedAxisXOut(targetOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() })
},
popEnterTransition = {
materialSharedAxisXIn(initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() })
scaleIn(
animationSpec = tween(durationMillis = 350, easing = EmphasizedDecelerate),
initialScale = INITIAL_SCALE_FACTOR,
) + materialSharedAxisXIn(initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() })
},
popExitTransition = {
materialSharedAxisXOut(targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() })
materialSharedAxisXOut(targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() }) +
scaleOut(
targetScale = INITIAL_SCALE_FACTOR,
animationSpec = tween(durationMillis = 350, easing = EmphasizedAccelerate),
)
},
content = content
content = content,
)
fun NavGraphBuilder.animatedComposableLegacy(
route: String,
arguments: List<NamedNavArgument> = emptyList(),
deepLinks: List<NavDeepLink> = emptyList(),
content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
) = composable(
route = route,
arguments = arguments,
deepLinks = deepLinks,
enterTransition = {
materialSharedAxisXIn(initialOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() })
},
exitTransition = {
materialSharedAxisXOut(targetOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() })
},
popEnterTransition = {
materialSharedAxisXIn(initialOffsetX = { -(it * INITIAL_OFFSET_FACTOR).toInt() })
},
popExitTransition = {
materialSharedAxisXOut(targetOffsetX = { (it * INITIAL_OFFSET_FACTOR).toInt() })
},
content = content
)

View File

@ -204,9 +204,6 @@ fun FeedsPage(
}
}
BackHandler(true) {
context.findActivity()?.moveTaskToBack(false)
}
RYScaffold(
topBarTonalElevation = topBarTonalElevation.value.dp,