diff --git a/androidApp/src/main/assets/app_icon.png b/androidApp/src/main/assets/app_icon.png
index 53fc536..d4491c3 100644
Binary files a/androidApp/src/main/assets/app_icon.png and b/androidApp/src/main/assets/app_icon.png differ
diff --git a/androidApp/src/main/res/drawable/ic_launcher_background.xml b/androidApp/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index e93e11a..0000000
--- a/androidApp/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_launcher_foreground.xml b/androidApp/src/main/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index 2b068d1..0000000
--- a/androidApp/src/main/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index eca70cf..0000000
--- a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index eca70cf..0000000
--- a/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png
index a571e60..9c67d11 100644
Binary files a/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png and b/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png
index 61da551..9c67d11 100644
Binary files a/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png
index c41dd28..8c16c78 100644
Binary files a/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png and b/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png
index db5080a..8c16c78 100644
Binary files a/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png
index 6dba46d..333be02 100644
Binary files a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png and b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png
index da31a87..333be02 100644
Binary files a/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 15ac681..78b044c 100644
Binary files a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
index b216f2d..78b044c 100644
Binary files a/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index f25a419..9a9fedb 100644
Binary files a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
index e96783c..9a9fedb 100644
Binary files a/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 997fd04..3b1fe4e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -16,6 +16,7 @@ material3 = "1.10.0-alpha05"
kotlinx-datetime = "0.8.0"
tyme4kt = "1.4.5"
sketch = "4.4.0"
+navigationevent = "1.0.1"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
@@ -34,6 +35,7 @@ tyme4kt = { module = "cn.6tail:tyme4kt", version.ref = "tyme4kt" }
sketch-compose = { module = "io.github.panpf.sketch4:sketch-compose", version.ref = "sketch" }
sketch-animated-gif = { module = "io.github.panpf.sketch4:sketch-animated-gif", version.ref = "sketch" }
sketch-compose-resources = { module = "io.github.panpf.sketch4:sketch-compose-resources", version.ref = "sketch" }
+navigationevent-compose = { module = "org.jetbrains.androidx.navigationevent:navigationevent-compose", version.ref = "navigationevent" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index e087e53..b8312ec 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -49,6 +49,7 @@ kotlin {
implementation(libs.sketch.compose)
implementation(libs.sketch.animated.gif)
implementation(libs.sketch.compose.resources)
+ implementation(libs.navigationevent.compose)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
diff --git a/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt b/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt
index 6b4d872..f1ae397 100644
--- a/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt
+++ b/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt
@@ -2,8 +2,11 @@ package plus.rua.project
import android.os.Build
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.rememberCoroutineScope
-import kotlinx.coroutines.CancellationException
+import androidx.compose.runtime.LaunchedEffect
+import androidx.navigationevent.NavigationEventInfo
+import androidx.navigationevent.NavigationEventTransitionState
+import androidx.navigationevent.compose.NavigationBackHandler
+import androidx.navigationevent.compose.rememberNavigationEventState
class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
@@ -22,19 +25,19 @@ actual fun PredictiveBackHandler(
onBack: () -> Unit,
onCancel: () -> Unit
) {
- if (Build.VERSION.SDK_INT >= 34) {
- rememberCoroutineScope()
- androidx.activity.compose.PredictiveBackHandler(enabled) { progress ->
- try {
- progress.collect { backEvent ->
- onProgress(backEvent.progress)
- }
- onBack()
- } catch (e: CancellationException) {
- onCancel()
- }
+ val navState = rememberNavigationEventState(NavigationEventInfo.None)
+
+ NavigationBackHandler(
+ state = navState,
+ isBackEnabled = enabled,
+ onBackCancelled = onCancel,
+ onBackCompleted = onBack
+ )
+
+ LaunchedEffect(navState.transitionState) {
+ val ts = navState.transitionState
+ if (ts is NavigationEventTransitionState.InProgress) {
+ onProgress(ts.latestEvent.progress)
}
- } else {
- androidx.activity.compose.BackHandler(enabled = enabled, onBack = onBack)
}
}
diff --git a/shared/src/commonMain/composeResources/files/app_icon.png b/shared/src/commonMain/composeResources/files/app_icon.png
index 53fc536..d4491c3 100644
Binary files a/shared/src/commonMain/composeResources/files/app_icon.png and b/shared/src/commonMain/composeResources/files/app_icon.png differ
diff --git a/shared/src/commonMain/kotlin/plus/rua/project/App.kt b/shared/src/commonMain/kotlin/plus/rua/project/App.kt
index 58956fe..8e14e37 100644
--- a/shared/src/commonMain/kotlin/plus/rua/project/App.kt
+++ b/shared/src/commonMain/kotlin/plus/rua/project/App.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -32,6 +33,35 @@ import plus.rua.project.ui.lerp
private enum class Screen { Main, About, Licenses }
+/** 返回手势动画:顶层页面滑出 + 淡出 + 缩小 + 圆角阴影 */
+private fun GraphicsLayerScope.applyDismissTransform(progress: Float) {
+ translationX = progress * size.width * 0.5f
+ scaleX = 1f - progress * 0.08f
+ scaleY = 1f - progress * 0.08f
+ alpha = 1f - progress * 0.8f
+ shadowElevation = 32.dp.toPx() * progress
+ shape = RoundedCornerShape(28.dp * progress)
+ clip = progress > 0.01f
+}
+
+/** 底层页面缩放:随返回进度从 baseScale 放大到 1.0 */
+private fun GraphicsLayerScope.applyRevealTransform(
+ progress: Float,
+ forwardProgress: Float,
+ isForwardAnimating: Boolean
+) {
+ val baseScale = 0.92f + 0.08f * progress
+ val scale = if (isForwardAnimating) lerp(1f, baseScale, forwardProgress) else baseScale
+ scaleX = scale
+ scaleY = scale
+}
+
+/** 前向导航动画:新页面从右侧滑入 */
+private fun GraphicsLayerScope.applyEnterTransform(progress: Float) {
+ translationX = (1f - progress) * size.width
+ alpha = progress
+}
+
/**
* 应用入口 Composable,根据系统主题切换明暗 ColorScheme 并管理页面导航。
*
@@ -59,7 +89,7 @@ fun App() {
scope.launch {
backAnimProgress.snapTo(backProgress)
backProgress = 0f
- backAnimProgress.animateTo(1f, tween(200, easing = FastOutSlowInEasing))
+ backAnimProgress.animateTo(1f, tween(250, easing = FastOutSlowInEasing))
currentScreen = when (currentScreen) {
Screen.About -> Screen.Main
Screen.Licenses -> Screen.About
@@ -96,14 +126,11 @@ fun App() {
CalendarMonthView(
modifier = Modifier.graphicsLayer {
if (currentScreen != Screen.Main) {
- val baseScale = 0.92f + 0.08f * effectiveBackProgress
- val scale = if (forwardTarget != null) {
- lerp(1f, baseScale, forwardProgress.value)
- } else {
- baseScale
- }
- scaleX = scale
- scaleY = scale
+ applyRevealTransform(
+ effectiveBackProgress,
+ forwardProgress.value,
+ forwardTarget != null
+ )
}
},
onNavigateToAbout = { navigateTo(Screen.About) }
@@ -120,34 +147,18 @@ fun App() {
},
modifier = Modifier.graphicsLayer {
when (currentScreen) {
- Screen.Licenses -> {
- val baseScale = 0.92f + 0.08f * effectiveBackProgress
- val scale = if (forwardTarget == Screen.Licenses) {
- lerp(1f, baseScale, forwardProgress.value)
- } else {
- baseScale
- }
- scaleX = scale
- scaleY = scale
- }
+ Screen.Licenses -> applyRevealTransform(
+ effectiveBackProgress,
+ forwardProgress.value,
+ forwardTarget == Screen.Licenses
+ )
Screen.About -> {
val bp = effectiveBackProgress
val fp = forwardProgress.value
when {
- bp > 0.001f -> {
- translationX = bp * size.width * 0.3f
- scaleX = 1f - bp * 0.05f
- scaleY = 1f - bp * 0.05f
- shadowElevation = 32.dp.toPx() * bp
- shape = RoundedCornerShape(28.dp * bp)
- clip = bp > 0.01f
- }
-
- fp < 0.999f && forwardTarget == Screen.About -> {
- translationX = (1f - fp) * size.width
- alpha = fp
- }
+ bp > 0.001f -> applyDismissTransform(bp)
+ fp < 0.999f && forwardTarget == Screen.About -> applyEnterTransform(fp)
}
}
@@ -165,19 +176,8 @@ fun App() {
val bp = effectiveBackProgress
val fp = forwardProgress.value
when {
- bp > 0.001f -> {
- translationX = bp * size.width * 0.3f
- scaleX = 1f - bp * 0.05f
- scaleY = 1f - bp * 0.05f
- shadowElevation = 32.dp.toPx() * bp
- shape = RoundedCornerShape(28.dp * bp)
- clip = bp > 0.01f
- }
-
- fp < 0.999f && forwardTarget == Screen.Licenses -> {
- translationX = (1f - fp) * size.width
- alpha = fp
- }
+ bp > 0.001f -> applyDismissTransform(bp)
+ fp < 0.999f && forwardTarget == Screen.Licenses -> applyEnterTransform(fp)
}
}
)