From fccac1111836bbf3e0cc4c75b690f58f41d7c38a Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Fri, 24 Oct 2025 13:21:59 +0800 Subject: [PATCH] Optimize navigation transition (#45, #167, #178) --- app/build.gradle.kts | 4 ++ .../com/bintianqi/owndroid/MainActivity.kt | 16 +++--- .../com/bintianqi/owndroid/ui/Animations.kt | 52 ------------------- .../bintianqi/owndroid/ui/NavTransition.kt | 45 ++++++++++++++++ 4 files changed, 59 insertions(+), 58 deletions(-) delete mode 100644 app/src/main/java/com/bintianqi/owndroid/ui/Animations.kt create mode 100644 app/src/main/java/com/bintianqi/owndroid/ui/NavTransition.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 88b5533..84c2009 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -44,6 +44,10 @@ android { debug { signingConfig = signingConfigs.getByName("defaultSignature") } + create("fastDebug") { + initWith(getByName("debug")) + isDebuggable = false + } } compileOptions { sourceCompatibility = JavaVersion.VERSION_21 diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index cffa309..c090955 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -228,7 +228,7 @@ import com.bintianqi.owndroid.dpm.WorkModesScreen import com.bintianqi.owndroid.dpm.WorkProfile import com.bintianqi.owndroid.dpm.WorkProfileScreen import com.bintianqi.owndroid.dpm.dhizukuErrorStatus -import com.bintianqi.owndroid.ui.Animations +import com.bintianqi.owndroid.ui.NavTransition import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import kotlinx.serialization.Serializable import java.util.Locale @@ -275,7 +275,11 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { val focusMgr = LocalFocusManager.current val lifecycleOwner = LocalLifecycleOwner.current fun navigateUp() { navController.navigateUp() } - fun navigate(destination: Any) { navController.navigate(destination) } + fun navigate(destination: Any) { + navController.navigate(destination) { + launchSingleTop = true + } + } fun choosePackage() { navController.navigate(ApplicationsList(false)) } @@ -293,10 +297,10 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { .fillMaxSize() .background(colorScheme.background) .pointerInput(Unit) { detectTapGestures(onTap = { focusMgr.clearFocus() }) }, - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition + enterTransition = { NavTransition.enterTransition }, + exitTransition = { NavTransition.exitTransition }, + popEnterTransition = { NavTransition.popEnterTransition }, + popExitTransition = { NavTransition.popExitTransition } ) { composable { HomeScreen(::navigate) } composable { diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/Animations.kt b/app/src/main/java/com/bintianqi/owndroid/ui/Animations.kt deleted file mode 100644 index 90c364a..0000000 --- a/app/src/main/java/com/bintianqi/owndroid/ui/Animations.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.bintianqi.owndroid.ui - -import androidx.compose.animation.* -import androidx.compose.animation.core.* -import androidx.compose.ui.unit.IntOffset -import androidx.navigation.NavBackStackEntry - -object Animations { - private const val INITIAL_OFFSET_VALUE = 96 - private const val TARGET_OFFSET_VALUE = 96 - - private val bezier = CubicBezierEasing(0.20f, 0.85f, 0.0f, 1f) - - private val tween: FiniteAnimationSpec = tween(durationMillis = 550, easing = bezier, delayMillis = 50) - - val navHostEnterTransition: AnimatedContentTransitionScope.() -> EnterTransition = { - fadeIn(tween(100, easing = LinearEasing)) + - slideIntoContainer( - animationSpec = tween, - towards = AnimatedContentTransitionScope.SlideDirection.End, - initialOffset = { INITIAL_OFFSET_VALUE } - ) - } - - val navHostExitTransition: AnimatedContentTransitionScope.() -> ExitTransition = { - fadeOut(tween(100, easing = LinearEasing)) + - slideOutOfContainer( - animationSpec = tween, - towards = AnimatedContentTransitionScope.SlideDirection.Start, - targetOffset = { -TARGET_OFFSET_VALUE } - ) - } - - val navHostPopEnterTransition: AnimatedContentTransitionScope.() -> EnterTransition = { - fadeIn(tween(100, easing = LinearEasing)) + - slideIntoContainer( - animationSpec = tween, - towards = AnimatedContentTransitionScope.SlideDirection.End, - initialOffset = { -INITIAL_OFFSET_VALUE } - ) - } - - val navHostPopExitTransition: AnimatedContentTransitionScope.() -> ExitTransition = { - fadeOut(tween(100, easing = LinearEasing)) + - slideOutOfContainer( - animationSpec = tween, - towards = AnimatedContentTransitionScope.SlideDirection.Start, - targetOffset = { TARGET_OFFSET_VALUE } - ) - } - -} diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/NavTransition.kt b/app/src/main/java/com/bintianqi/owndroid/ui/NavTransition.kt new file mode 100644 index 0000000..d9dd4df --- /dev/null +++ b/app/src/main/java/com/bintianqi/owndroid/ui/NavTransition.kt @@ -0,0 +1,45 @@ +package com.bintianqi.owndroid.ui + +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.CubicBezierEasing +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally + +/** + * Learned from AOSP's Activity animation + * `frameworks/base/core/res/res/anim/activity_xxx_xxx.xml` + */ +object NavTransition { + val StandardAccelerateEasing = CubicBezierEasing(0.3F, 0F, 1F, 1F) + + val enterTransition: EnterTransition = slideInHorizontally( + tween(450, easing = FastOutSlowInEasing), + { 96 } + ) + fadeIn( + tween(83, 50, LinearEasing) + ) + + val exitTransition: ExitTransition = slideOutHorizontally( + tween(450, easing = StandardAccelerateEasing), + { -96 } + ) + fadeOut(tween(100, 200, LinearEasing)) + + val popEnterTransition: EnterTransition = slideInHorizontally( + tween(450, easing = FastOutSlowInEasing), + { -96 } + ) + + val popExitTransition: ExitTransition = + slideOutHorizontally( + tween(450, easing = FastOutSlowInEasing), + { 96 } + ) + fadeOut( + tween(83, 35, LinearEasing) + ) +}