diff --git a/CLAUDE.md b/CLAUDE.md index 42b889b..6abfb39 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,13 +12,22 @@ YaYa is a calendar app built with Kotlin Multiplatform (KMP) + Compose Multiplat # Build Android debug APK ./gradlew :androidApp:assembleDebug -# Run shared module tests +# Install Android debug APK to connected device +./gradlew :androidApp:installDebug + +# Run all shared module tests +./gradlew :shared:allTests + +# Run shared module tests on Android host only ./gradlew :shared:testAndroidHostTest # Run a single test class ./gradlew :shared:testAndroidHostTest --tests "plus.rua.project.ui.CalendarUtilsTest" -# Build iOS app — open iosApp/ in Xcode and run from there +# Generate iOS framework (required before first Xcode open or after clean) +./gradlew :shared:generateDummyFramework + +# Build iOS app — open iosApp/iosApp.xcworkspace in Xcode and run from there ``` Gradle configuration cache and build cache are enabled by default (`gradle.properties`). @@ -62,13 +71,17 @@ CalendarMonthView ← top-level screen (MonthHeader + WeekdayHeader + p `CalendarViewModel` holds `selectedDate` and `isCollapsed` state, computes month day grids (6×7=42 cells) and ISO week numbers. Week starts on Monday (ISO 8601). +**Performance tracing:** `ComposeTrace.kt` provides `composeTraceBeginSection`/`composeTraceEndSection` via expect/actual — Android routes to `android.os.Trace`, iOS is a no-op. Custom markers are inserted at key points (e.g., `MonthView:Compose`, `YearView:Compose`, `VM:collapseProgress`) for Perfetto/Systrace analysis. See `DEVELOPMENT.md` for trace recording and Python parsing scripts. + ## Key Dependencies - Kotlin 2.3.21, Compose Multiplatform 1.11.0, Material 3 1.10.0-alpha05 - `kotlinx-datetime` 0.8.0 for all date logic (no java.util.Calendar) - `tyme4kt` for Chinese traditional calendar (lunar dates, solar terms, festivals) +- `sketch` 4.4.0 for animated GIF display (`AsyncImage` with `sketch-animated-gif`) - AGP 9.2.1, compileSdk/targetSdk 37, minSdk 24 - JVM target: 17 +- R8 full mode enabled (`android.enableR8.fullMode=true`) ## Conventions diff --git a/README.md b/README.md index 1adebe9..54e0f8b 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ # YaYa -基于 Kotlin Multiplatform 与 Compose Multiplatform 的日历应用,Android 和 iOS 共享 UI。 +基于 Kotlin Multiplatform 与 Compose Multiplatform 的跨平台日历应用,Android 与 iOS 共享同一套 UI 与业务逻辑。 -月视图与周视图之间支持流畅的折叠/展开过渡——拖拽切换,弹簧动画自动吸附。无限分页,ISO 8601 周起始,Material 3。 +## 特性 -## 构建 - -```bash -# Android -./gradlew :androidApp:assembleDebug - -# iOS — 在 Xcode 中打开 iosApp/ 运行 -``` +- **流畅的视图切换** —— 月视图、周视图、年视图三种模式,拖拽手势驱动月↔周折叠,弹簧动画自动吸附 +- **无限滑动分页** —— 基于 `Int.MAX_VALUE` 的虚拟分页,前后无边界翻页 +- **完整中式日历** —— 公历 + 农历 + 二十四节气 + 传统节日,ISO 8601 周起始(周一) +- **个人排班周期** —— 自定义工作/休息循环,与公共节假日独立 +- **Material 3 设计** —— 动态配色,深色模式 ## 技术栈 -- Kotlin 2.3 · Compose Multiplatform 1.10 · Material 3 +- Kotlin 2.3 · Compose Multiplatform 1.11 · Material 3 - `kotlinx-datetime` 处理所有日期逻辑 -- 双模块:`:shared`(UI + 逻辑)· `:androidApp`(薄壳) +- `tyme4kt` 提供农历、节气与传统节日 +- `sketch` 渲染 GIF 动画 +- 双模块:`:shared`(UI + 逻辑) · `:androidApp`(薄壳) +- iOS 入口为 `MainViewController.kt`,Xcode 工程位于 `iosApp/` diff --git a/androidApp/proguard-rules.pro b/androidApp/proguard-rules.pro index 540142c..11d8d66 100644 --- a/androidApp/proguard-rules.pro +++ b/androidApp/proguard-rules.pro @@ -8,9 +8,6 @@ # tyme4kt (Chinese traditional calendar) -keep class cn.tyme.** { *; } -# Compose runtime reflective lookups --keep class androidx.compose.runtime.** { *; } - # ViewModel (used by CalendarViewModel) -keep class * extends androidx.lifecycle.ViewModel { *; } diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml index 298cd22..edf4072 100644 --- a/androidApp/src/main/AndroidManifest.xml +++ b/androidApp/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ CADisableMinimumFrameDurationOnPhone + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 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 1f95c7e..f6320fd 100644 --- a/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt +++ b/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt @@ -1,9 +1,41 @@ package plus.rua.project import android.os.Build +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.launch class AndroidPlatform : Platform { override val name: String = "Android ${Build.VERSION.SDK_INT}" } -actual fun getPlatform(): Platform = AndroidPlatform() \ No newline at end of file +actual fun getPlatform(): Platform = AndroidPlatform() + +actual fun getGifUri(gifFile: String): String = "file:///android_asset/gifs/$gifFile" + +actual fun getAppIconUri(): String = "file:///android_asset/app_icon.png" + +@Composable +actual fun PredictiveBackHandler( + enabled: Boolean, + onProgress: (Float) -> Unit, + onBack: () -> Unit, + onCancel: () -> Unit +) { + if (Build.VERSION.SDK_INT >= 34) { + val scope = rememberCoroutineScope() + androidx.activity.compose.PredictiveBackHandler(enabled) { progress -> + try { + progress.collect { backEvent -> + onProgress(backEvent.progress) + } + onBack() + } catch (e: CancellationException) { + onCancel() + } + } + } else { + androidx.activity.compose.BackHandler(enabled = enabled, onBack = onBack) + } +} diff --git a/shared/src/commonMain/composeResources/files/001.gif b/shared/src/commonMain/composeResources/files/001.gif new file mode 100644 index 0000000..f9d68f9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/001.gif differ diff --git a/shared/src/commonMain/composeResources/files/002.gif b/shared/src/commonMain/composeResources/files/002.gif new file mode 100644 index 0000000..fcbd106 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/002.gif differ diff --git a/shared/src/commonMain/composeResources/files/003.gif b/shared/src/commonMain/composeResources/files/003.gif new file mode 100644 index 0000000..588f329 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/003.gif differ diff --git a/shared/src/commonMain/composeResources/files/004.gif b/shared/src/commonMain/composeResources/files/004.gif new file mode 100644 index 0000000..d03785e Binary files /dev/null and b/shared/src/commonMain/composeResources/files/004.gif differ diff --git a/shared/src/commonMain/composeResources/files/005.gif b/shared/src/commonMain/composeResources/files/005.gif new file mode 100644 index 0000000..5822a81 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/005.gif differ diff --git a/shared/src/commonMain/composeResources/files/006.gif b/shared/src/commonMain/composeResources/files/006.gif new file mode 100644 index 0000000..297e3b1 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/006.gif differ diff --git a/shared/src/commonMain/composeResources/files/007.gif b/shared/src/commonMain/composeResources/files/007.gif new file mode 100644 index 0000000..78954a9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/007.gif differ diff --git a/shared/src/commonMain/composeResources/files/008.gif b/shared/src/commonMain/composeResources/files/008.gif new file mode 100644 index 0000000..7f91570 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/008.gif differ diff --git a/shared/src/commonMain/composeResources/files/009.gif b/shared/src/commonMain/composeResources/files/009.gif new file mode 100644 index 0000000..c3645a4 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/009.gif differ diff --git a/shared/src/commonMain/composeResources/files/010.gif b/shared/src/commonMain/composeResources/files/010.gif new file mode 100644 index 0000000..6b05971 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/010.gif differ diff --git a/shared/src/commonMain/composeResources/files/011.gif b/shared/src/commonMain/composeResources/files/011.gif new file mode 100644 index 0000000..1078f42 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/011.gif differ diff --git a/shared/src/commonMain/composeResources/files/012.gif b/shared/src/commonMain/composeResources/files/012.gif new file mode 100644 index 0000000..1b6785a Binary files /dev/null and b/shared/src/commonMain/composeResources/files/012.gif differ diff --git a/shared/src/commonMain/composeResources/files/013.gif b/shared/src/commonMain/composeResources/files/013.gif new file mode 100644 index 0000000..ba5bff4 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/013.gif differ diff --git a/shared/src/commonMain/composeResources/files/014.gif b/shared/src/commonMain/composeResources/files/014.gif new file mode 100644 index 0000000..ad286e7 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/014.gif differ diff --git a/shared/src/commonMain/composeResources/files/015.gif b/shared/src/commonMain/composeResources/files/015.gif new file mode 100644 index 0000000..804cc6f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/015.gif differ diff --git a/shared/src/commonMain/composeResources/files/016.gif b/shared/src/commonMain/composeResources/files/016.gif new file mode 100644 index 0000000..a11443d Binary files /dev/null and b/shared/src/commonMain/composeResources/files/016.gif differ diff --git a/shared/src/commonMain/composeResources/files/017.gif b/shared/src/commonMain/composeResources/files/017.gif new file mode 100644 index 0000000..e060254 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/017.gif differ diff --git a/shared/src/commonMain/composeResources/files/018.gif b/shared/src/commonMain/composeResources/files/018.gif new file mode 100644 index 0000000..700203a Binary files /dev/null and b/shared/src/commonMain/composeResources/files/018.gif differ diff --git a/shared/src/commonMain/composeResources/files/019.gif b/shared/src/commonMain/composeResources/files/019.gif new file mode 100644 index 0000000..1f56909 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/019.gif differ diff --git a/shared/src/commonMain/composeResources/files/020.gif b/shared/src/commonMain/composeResources/files/020.gif new file mode 100644 index 0000000..58683be Binary files /dev/null and b/shared/src/commonMain/composeResources/files/020.gif differ diff --git a/shared/src/commonMain/composeResources/files/021.gif b/shared/src/commonMain/composeResources/files/021.gif new file mode 100644 index 0000000..2a5dac3 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/021.gif differ diff --git a/shared/src/commonMain/composeResources/files/022.gif b/shared/src/commonMain/composeResources/files/022.gif new file mode 100644 index 0000000..a5b7bc9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/022.gif differ diff --git a/shared/src/commonMain/composeResources/files/023.gif b/shared/src/commonMain/composeResources/files/023.gif new file mode 100644 index 0000000..91f8aa9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/023.gif differ diff --git a/shared/src/commonMain/composeResources/files/024.gif b/shared/src/commonMain/composeResources/files/024.gif new file mode 100644 index 0000000..1d7dc74 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/024.gif differ diff --git a/shared/src/commonMain/composeResources/files/025.gif b/shared/src/commonMain/composeResources/files/025.gif new file mode 100644 index 0000000..7b536d4 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/025.gif differ diff --git a/shared/src/commonMain/composeResources/files/026.gif b/shared/src/commonMain/composeResources/files/026.gif new file mode 100644 index 0000000..1aa62d2 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/026.gif differ diff --git a/shared/src/commonMain/composeResources/files/027.gif b/shared/src/commonMain/composeResources/files/027.gif new file mode 100644 index 0000000..6ce4179 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/027.gif differ diff --git a/shared/src/commonMain/composeResources/files/028.gif b/shared/src/commonMain/composeResources/files/028.gif new file mode 100644 index 0000000..a035501 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/028.gif differ diff --git a/shared/src/commonMain/composeResources/files/029.gif b/shared/src/commonMain/composeResources/files/029.gif new file mode 100644 index 0000000..4c99ec9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/029.gif differ diff --git a/shared/src/commonMain/composeResources/files/030.gif b/shared/src/commonMain/composeResources/files/030.gif new file mode 100644 index 0000000..ae868c7 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/030.gif differ diff --git a/shared/src/commonMain/composeResources/files/031.gif b/shared/src/commonMain/composeResources/files/031.gif new file mode 100644 index 0000000..d6ed782 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/031.gif differ diff --git a/shared/src/commonMain/composeResources/files/puppy_1.gif b/shared/src/commonMain/composeResources/files/032.gif similarity index 100% rename from shared/src/commonMain/composeResources/files/puppy_1.gif rename to shared/src/commonMain/composeResources/files/032.gif diff --git a/shared/src/commonMain/composeResources/files/033.gif b/shared/src/commonMain/composeResources/files/033.gif new file mode 100644 index 0000000..edff147 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/033.gif differ diff --git a/shared/src/commonMain/composeResources/files/034.gif b/shared/src/commonMain/composeResources/files/034.gif new file mode 100644 index 0000000..f2476f8 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/034.gif differ diff --git a/shared/src/commonMain/composeResources/files/035.gif b/shared/src/commonMain/composeResources/files/035.gif new file mode 100644 index 0000000..c148ddc Binary files /dev/null and b/shared/src/commonMain/composeResources/files/035.gif differ diff --git a/shared/src/commonMain/composeResources/files/036.gif b/shared/src/commonMain/composeResources/files/036.gif new file mode 100644 index 0000000..5b3926b Binary files /dev/null and b/shared/src/commonMain/composeResources/files/036.gif differ diff --git a/shared/src/commonMain/composeResources/files/037.gif b/shared/src/commonMain/composeResources/files/037.gif new file mode 100644 index 0000000..d711cfc Binary files /dev/null and b/shared/src/commonMain/composeResources/files/037.gif differ diff --git a/shared/src/commonMain/composeResources/files/038.gif b/shared/src/commonMain/composeResources/files/038.gif new file mode 100644 index 0000000..be3bb26 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/038.gif differ diff --git a/shared/src/commonMain/composeResources/files/039.gif b/shared/src/commonMain/composeResources/files/039.gif new file mode 100644 index 0000000..bff475a Binary files /dev/null and b/shared/src/commonMain/composeResources/files/039.gif differ diff --git a/shared/src/commonMain/composeResources/files/040.gif b/shared/src/commonMain/composeResources/files/040.gif new file mode 100644 index 0000000..396ebaf Binary files /dev/null and b/shared/src/commonMain/composeResources/files/040.gif differ diff --git a/shared/src/commonMain/composeResources/files/041.gif b/shared/src/commonMain/composeResources/files/041.gif new file mode 100644 index 0000000..a035501 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/041.gif differ diff --git a/shared/src/commonMain/composeResources/files/042.gif b/shared/src/commonMain/composeResources/files/042.gif new file mode 100644 index 0000000..8ef5dc6 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/042.gif differ diff --git a/shared/src/commonMain/composeResources/files/043.gif b/shared/src/commonMain/composeResources/files/043.gif new file mode 100644 index 0000000..87bcc23 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/043.gif differ diff --git a/shared/src/commonMain/composeResources/files/044.gif b/shared/src/commonMain/composeResources/files/044.gif new file mode 100644 index 0000000..72441c8 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/044.gif differ diff --git a/shared/src/commonMain/composeResources/files/045.gif b/shared/src/commonMain/composeResources/files/045.gif new file mode 100644 index 0000000..4af1e6e Binary files /dev/null and b/shared/src/commonMain/composeResources/files/045.gif differ diff --git a/shared/src/commonMain/composeResources/files/046.gif b/shared/src/commonMain/composeResources/files/046.gif new file mode 100644 index 0000000..f8f1f42 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/046.gif differ diff --git a/shared/src/commonMain/composeResources/files/047.gif b/shared/src/commonMain/composeResources/files/047.gif new file mode 100644 index 0000000..737ac61 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/047.gif differ diff --git a/shared/src/commonMain/composeResources/files/048.gif b/shared/src/commonMain/composeResources/files/048.gif new file mode 100644 index 0000000..cbaea03 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/048.gif differ diff --git a/shared/src/commonMain/composeResources/files/049.gif b/shared/src/commonMain/composeResources/files/049.gif new file mode 100644 index 0000000..40194a1 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/049.gif differ diff --git a/shared/src/commonMain/composeResources/files/050.gif b/shared/src/commonMain/composeResources/files/050.gif new file mode 100644 index 0000000..9404581 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/050.gif differ diff --git a/shared/src/commonMain/composeResources/files/051.gif b/shared/src/commonMain/composeResources/files/051.gif new file mode 100644 index 0000000..3470ead Binary files /dev/null and b/shared/src/commonMain/composeResources/files/051.gif differ diff --git a/shared/src/commonMain/composeResources/files/052.gif b/shared/src/commonMain/composeResources/files/052.gif new file mode 100644 index 0000000..3c6fe65 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/052.gif differ diff --git a/shared/src/commonMain/composeResources/files/053.gif b/shared/src/commonMain/composeResources/files/053.gif new file mode 100644 index 0000000..f3fc31e Binary files /dev/null and b/shared/src/commonMain/composeResources/files/053.gif differ diff --git a/shared/src/commonMain/composeResources/files/054.gif b/shared/src/commonMain/composeResources/files/054.gif new file mode 100644 index 0000000..c19e21d Binary files /dev/null and b/shared/src/commonMain/composeResources/files/054.gif differ diff --git a/shared/src/commonMain/composeResources/files/055.gif b/shared/src/commonMain/composeResources/files/055.gif new file mode 100644 index 0000000..8e1741a Binary files /dev/null and b/shared/src/commonMain/composeResources/files/055.gif differ diff --git a/shared/src/commonMain/composeResources/files/056.gif b/shared/src/commonMain/composeResources/files/056.gif new file mode 100644 index 0000000..f9cbec7 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/056.gif differ diff --git a/shared/src/commonMain/composeResources/files/057.gif b/shared/src/commonMain/composeResources/files/057.gif new file mode 100644 index 0000000..7809753 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/057.gif differ diff --git a/shared/src/commonMain/composeResources/files/058.gif b/shared/src/commonMain/composeResources/files/058.gif new file mode 100644 index 0000000..b84f939 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/058.gif differ diff --git a/shared/src/commonMain/composeResources/files/059.gif b/shared/src/commonMain/composeResources/files/059.gif new file mode 100644 index 0000000..29c26e4 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/059.gif differ diff --git a/shared/src/commonMain/composeResources/files/060.gif b/shared/src/commonMain/composeResources/files/060.gif new file mode 100644 index 0000000..ed932b9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/060.gif differ diff --git a/shared/src/commonMain/composeResources/files/061.gif b/shared/src/commonMain/composeResources/files/061.gif new file mode 100644 index 0000000..e6e93c3 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/061.gif differ diff --git a/shared/src/commonMain/composeResources/files/062.gif b/shared/src/commonMain/composeResources/files/062.gif new file mode 100644 index 0000000..d1d6783 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/062.gif differ diff --git a/shared/src/commonMain/composeResources/files/063.gif b/shared/src/commonMain/composeResources/files/063.gif new file mode 100644 index 0000000..7f165c3 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/063.gif differ diff --git a/shared/src/commonMain/composeResources/files/064.gif b/shared/src/commonMain/composeResources/files/064.gif new file mode 100644 index 0000000..b5807c5 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/064.gif differ diff --git a/shared/src/commonMain/composeResources/files/065.gif b/shared/src/commonMain/composeResources/files/065.gif new file mode 100644 index 0000000..533c058 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/065.gif differ diff --git a/shared/src/commonMain/composeResources/files/066.gif b/shared/src/commonMain/composeResources/files/066.gif new file mode 100644 index 0000000..b437247 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/066.gif differ diff --git a/shared/src/commonMain/composeResources/files/067.gif b/shared/src/commonMain/composeResources/files/067.gif new file mode 100644 index 0000000..bc7e558 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/067.gif differ diff --git a/shared/src/commonMain/composeResources/files/068.gif b/shared/src/commonMain/composeResources/files/068.gif new file mode 100644 index 0000000..ebe82ba Binary files /dev/null and b/shared/src/commonMain/composeResources/files/068.gif differ diff --git a/shared/src/commonMain/composeResources/files/069.gif b/shared/src/commonMain/composeResources/files/069.gif new file mode 100644 index 0000000..0665b0d Binary files /dev/null and b/shared/src/commonMain/composeResources/files/069.gif differ diff --git a/shared/src/commonMain/composeResources/files/070.gif b/shared/src/commonMain/composeResources/files/070.gif new file mode 100644 index 0000000..6d0cf12 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/070.gif differ diff --git a/shared/src/commonMain/composeResources/files/071.gif b/shared/src/commonMain/composeResources/files/071.gif new file mode 100644 index 0000000..2101450 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/071.gif differ diff --git a/shared/src/commonMain/composeResources/files/072.gif b/shared/src/commonMain/composeResources/files/072.gif new file mode 100644 index 0000000..87d5ee4 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/072.gif differ diff --git a/shared/src/commonMain/composeResources/files/073.gif b/shared/src/commonMain/composeResources/files/073.gif new file mode 100644 index 0000000..d4d6657 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/073.gif differ diff --git a/shared/src/commonMain/composeResources/files/074.gif b/shared/src/commonMain/composeResources/files/074.gif new file mode 100644 index 0000000..95599dc Binary files /dev/null and b/shared/src/commonMain/composeResources/files/074.gif differ diff --git a/shared/src/commonMain/composeResources/files/075.gif b/shared/src/commonMain/composeResources/files/075.gif new file mode 100644 index 0000000..08fe7ec Binary files /dev/null and b/shared/src/commonMain/composeResources/files/075.gif differ diff --git a/shared/src/commonMain/composeResources/files/076.gif b/shared/src/commonMain/composeResources/files/076.gif new file mode 100644 index 0000000..c529edb Binary files /dev/null and b/shared/src/commonMain/composeResources/files/076.gif differ diff --git a/shared/src/commonMain/composeResources/files/077.gif b/shared/src/commonMain/composeResources/files/077.gif new file mode 100644 index 0000000..64117fd Binary files /dev/null and b/shared/src/commonMain/composeResources/files/077.gif differ diff --git a/shared/src/commonMain/composeResources/files/078.gif b/shared/src/commonMain/composeResources/files/078.gif new file mode 100644 index 0000000..a08ba13 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/078.gif differ diff --git a/shared/src/commonMain/composeResources/files/079.gif b/shared/src/commonMain/composeResources/files/079.gif new file mode 100644 index 0000000..76f2359 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/079.gif differ diff --git a/shared/src/commonMain/composeResources/files/080.gif b/shared/src/commonMain/composeResources/files/080.gif new file mode 100644 index 0000000..b49e8df Binary files /dev/null and b/shared/src/commonMain/composeResources/files/080.gif differ diff --git a/shared/src/commonMain/composeResources/files/081.gif b/shared/src/commonMain/composeResources/files/081.gif new file mode 100644 index 0000000..428745f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/081.gif differ diff --git a/shared/src/commonMain/composeResources/files/082.gif b/shared/src/commonMain/composeResources/files/082.gif new file mode 100644 index 0000000..05b2f47 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/082.gif differ diff --git a/shared/src/commonMain/composeResources/files/083.gif b/shared/src/commonMain/composeResources/files/083.gif new file mode 100644 index 0000000..0125225 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/083.gif differ diff --git a/shared/src/commonMain/composeResources/files/084.gif b/shared/src/commonMain/composeResources/files/084.gif new file mode 100644 index 0000000..7c6fb18 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/084.gif differ diff --git a/shared/src/commonMain/composeResources/files/085.gif b/shared/src/commonMain/composeResources/files/085.gif new file mode 100644 index 0000000..84da828 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/085.gif differ diff --git a/shared/src/commonMain/composeResources/files/086.gif b/shared/src/commonMain/composeResources/files/086.gif new file mode 100644 index 0000000..ebdd4a9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/086.gif differ diff --git a/shared/src/commonMain/composeResources/files/087.gif b/shared/src/commonMain/composeResources/files/087.gif new file mode 100644 index 0000000..810f1bb Binary files /dev/null and b/shared/src/commonMain/composeResources/files/087.gif differ diff --git a/shared/src/commonMain/composeResources/files/088.gif b/shared/src/commonMain/composeResources/files/088.gif new file mode 100644 index 0000000..2aa37e6 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/088.gif differ diff --git a/shared/src/commonMain/composeResources/files/089.gif b/shared/src/commonMain/composeResources/files/089.gif new file mode 100644 index 0000000..2ce1520 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/089.gif differ diff --git a/shared/src/commonMain/composeResources/files/090.gif b/shared/src/commonMain/composeResources/files/090.gif new file mode 100644 index 0000000..920ab9f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/090.gif differ diff --git a/shared/src/commonMain/composeResources/files/091.gif b/shared/src/commonMain/composeResources/files/091.gif new file mode 100644 index 0000000..102a664 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/091.gif differ diff --git a/shared/src/commonMain/composeResources/files/092.gif b/shared/src/commonMain/composeResources/files/092.gif new file mode 100644 index 0000000..b6084ce Binary files /dev/null and b/shared/src/commonMain/composeResources/files/092.gif differ diff --git a/shared/src/commonMain/composeResources/files/093.gif b/shared/src/commonMain/composeResources/files/093.gif new file mode 100644 index 0000000..f9f3c37 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/093.gif differ diff --git a/shared/src/commonMain/composeResources/files/094.gif b/shared/src/commonMain/composeResources/files/094.gif new file mode 100644 index 0000000..e0de2f8 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/094.gif differ diff --git a/shared/src/commonMain/composeResources/files/095.gif b/shared/src/commonMain/composeResources/files/095.gif new file mode 100644 index 0000000..480a837 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/095.gif differ diff --git a/shared/src/commonMain/composeResources/files/096.gif b/shared/src/commonMain/composeResources/files/096.gif new file mode 100644 index 0000000..43ee629 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/096.gif differ diff --git a/shared/src/commonMain/composeResources/files/097.gif b/shared/src/commonMain/composeResources/files/097.gif new file mode 100644 index 0000000..8887d03 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/097.gif differ diff --git a/shared/src/commonMain/composeResources/files/098.gif b/shared/src/commonMain/composeResources/files/098.gif new file mode 100644 index 0000000..ed63c8c Binary files /dev/null and b/shared/src/commonMain/composeResources/files/098.gif differ diff --git a/shared/src/commonMain/composeResources/files/099.gif b/shared/src/commonMain/composeResources/files/099.gif new file mode 100644 index 0000000..e1d45be Binary files /dev/null and b/shared/src/commonMain/composeResources/files/099.gif differ diff --git a/shared/src/commonMain/composeResources/files/100.gif b/shared/src/commonMain/composeResources/files/100.gif new file mode 100644 index 0000000..1105fdb Binary files /dev/null and b/shared/src/commonMain/composeResources/files/100.gif differ diff --git a/shared/src/commonMain/composeResources/files/101.gif b/shared/src/commonMain/composeResources/files/101.gif new file mode 100644 index 0000000..1c7789f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/101.gif differ diff --git a/shared/src/commonMain/composeResources/files/102.gif b/shared/src/commonMain/composeResources/files/102.gif new file mode 100644 index 0000000..efbd5c6 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/102.gif differ diff --git a/shared/src/commonMain/composeResources/files/103.gif b/shared/src/commonMain/composeResources/files/103.gif new file mode 100644 index 0000000..8ac46fe Binary files /dev/null and b/shared/src/commonMain/composeResources/files/103.gif differ diff --git a/shared/src/commonMain/composeResources/files/104.gif b/shared/src/commonMain/composeResources/files/104.gif new file mode 100644 index 0000000..466da0c Binary files /dev/null and b/shared/src/commonMain/composeResources/files/104.gif differ diff --git a/shared/src/commonMain/composeResources/files/105.gif b/shared/src/commonMain/composeResources/files/105.gif new file mode 100644 index 0000000..28d98df Binary files /dev/null and b/shared/src/commonMain/composeResources/files/105.gif differ diff --git a/shared/src/commonMain/composeResources/files/106.gif b/shared/src/commonMain/composeResources/files/106.gif new file mode 100644 index 0000000..b1a0da8 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/106.gif differ diff --git a/shared/src/commonMain/composeResources/files/107.gif b/shared/src/commonMain/composeResources/files/107.gif new file mode 100644 index 0000000..cb4cf31 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/107.gif differ diff --git a/shared/src/commonMain/composeResources/files/108.gif b/shared/src/commonMain/composeResources/files/108.gif new file mode 100644 index 0000000..077734d Binary files /dev/null and b/shared/src/commonMain/composeResources/files/108.gif differ diff --git a/shared/src/commonMain/composeResources/files/109.gif b/shared/src/commonMain/composeResources/files/109.gif new file mode 100644 index 0000000..7253fd1 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/109.gif differ diff --git a/shared/src/commonMain/composeResources/files/110.gif b/shared/src/commonMain/composeResources/files/110.gif new file mode 100644 index 0000000..fc31eab Binary files /dev/null and b/shared/src/commonMain/composeResources/files/110.gif differ diff --git a/shared/src/commonMain/composeResources/files/111.gif b/shared/src/commonMain/composeResources/files/111.gif new file mode 100644 index 0000000..b85ec6f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/111.gif differ diff --git a/shared/src/commonMain/composeResources/files/112.gif b/shared/src/commonMain/composeResources/files/112.gif new file mode 100644 index 0000000..8ccb8bd Binary files /dev/null and b/shared/src/commonMain/composeResources/files/112.gif differ diff --git a/shared/src/commonMain/composeResources/files/113.gif b/shared/src/commonMain/composeResources/files/113.gif new file mode 100644 index 0000000..47b0d19 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/113.gif differ diff --git a/shared/src/commonMain/composeResources/files/114.gif b/shared/src/commonMain/composeResources/files/114.gif new file mode 100644 index 0000000..48dc1da Binary files /dev/null and b/shared/src/commonMain/composeResources/files/114.gif differ diff --git a/shared/src/commonMain/composeResources/files/115.gif b/shared/src/commonMain/composeResources/files/115.gif new file mode 100644 index 0000000..8378438 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/115.gif differ diff --git a/shared/src/commonMain/composeResources/files/116.gif b/shared/src/commonMain/composeResources/files/116.gif new file mode 100644 index 0000000..da95ec7 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/116.gif differ diff --git a/shared/src/commonMain/composeResources/files/117.gif b/shared/src/commonMain/composeResources/files/117.gif new file mode 100644 index 0000000..681d211 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/117.gif differ diff --git a/shared/src/commonMain/composeResources/files/118.gif b/shared/src/commonMain/composeResources/files/118.gif new file mode 100644 index 0000000..80fc126 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/118.gif differ diff --git a/shared/src/commonMain/composeResources/files/119.gif b/shared/src/commonMain/composeResources/files/119.gif new file mode 100644 index 0000000..62fc28c Binary files /dev/null and b/shared/src/commonMain/composeResources/files/119.gif differ diff --git a/shared/src/commonMain/composeResources/files/120.gif b/shared/src/commonMain/composeResources/files/120.gif new file mode 100644 index 0000000..703445f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/120.gif differ diff --git a/shared/src/commonMain/composeResources/files/121.gif b/shared/src/commonMain/composeResources/files/121.gif new file mode 100644 index 0000000..eda3613 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/121.gif differ diff --git a/shared/src/commonMain/composeResources/files/122.gif b/shared/src/commonMain/composeResources/files/122.gif new file mode 100644 index 0000000..1a3d798 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/122.gif differ diff --git a/shared/src/commonMain/composeResources/files/123.gif b/shared/src/commonMain/composeResources/files/123.gif new file mode 100644 index 0000000..23163a5 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/123.gif differ diff --git a/shared/src/commonMain/composeResources/files/124.gif b/shared/src/commonMain/composeResources/files/124.gif new file mode 100644 index 0000000..fbe3419 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/124.gif differ diff --git a/shared/src/commonMain/composeResources/files/125.gif b/shared/src/commonMain/composeResources/files/125.gif new file mode 100644 index 0000000..0be6a37 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/125.gif differ diff --git a/shared/src/commonMain/composeResources/files/126.gif b/shared/src/commonMain/composeResources/files/126.gif new file mode 100644 index 0000000..913a8cb Binary files /dev/null and b/shared/src/commonMain/composeResources/files/126.gif differ diff --git a/shared/src/commonMain/composeResources/files/127.gif b/shared/src/commonMain/composeResources/files/127.gif new file mode 100644 index 0000000..992fd68 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/127.gif differ diff --git a/shared/src/commonMain/composeResources/files/128.gif b/shared/src/commonMain/composeResources/files/128.gif new file mode 100644 index 0000000..2b9d625 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/128.gif differ diff --git a/shared/src/commonMain/composeResources/files/129.gif b/shared/src/commonMain/composeResources/files/129.gif new file mode 100644 index 0000000..d05dbad Binary files /dev/null and b/shared/src/commonMain/composeResources/files/129.gif differ diff --git a/shared/src/commonMain/composeResources/files/130.gif b/shared/src/commonMain/composeResources/files/130.gif new file mode 100644 index 0000000..812b1b0 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/130.gif differ diff --git a/shared/src/commonMain/composeResources/files/131.gif b/shared/src/commonMain/composeResources/files/131.gif new file mode 100644 index 0000000..a0ac549 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/131.gif differ diff --git a/shared/src/commonMain/composeResources/files/132.gif b/shared/src/commonMain/composeResources/files/132.gif new file mode 100644 index 0000000..24267b4 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/132.gif differ diff --git a/shared/src/commonMain/composeResources/files/133.gif b/shared/src/commonMain/composeResources/files/133.gif new file mode 100644 index 0000000..e932cc0 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/133.gif differ diff --git a/shared/src/commonMain/composeResources/files/134.gif b/shared/src/commonMain/composeResources/files/134.gif new file mode 100644 index 0000000..1e943f4 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/134.gif differ diff --git a/shared/src/commonMain/composeResources/files/135.gif b/shared/src/commonMain/composeResources/files/135.gif new file mode 100644 index 0000000..0256ee9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/135.gif differ diff --git a/shared/src/commonMain/composeResources/files/136.gif b/shared/src/commonMain/composeResources/files/136.gif new file mode 100644 index 0000000..f3b0730 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/136.gif differ diff --git a/shared/src/commonMain/composeResources/files/137.gif b/shared/src/commonMain/composeResources/files/137.gif new file mode 100644 index 0000000..bafc6e7 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/137.gif differ diff --git a/shared/src/commonMain/composeResources/files/138.gif b/shared/src/commonMain/composeResources/files/138.gif new file mode 100644 index 0000000..7582e8f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/138.gif differ diff --git a/shared/src/commonMain/composeResources/files/139.gif b/shared/src/commonMain/composeResources/files/139.gif new file mode 100644 index 0000000..96541b9 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/139.gif differ diff --git a/shared/src/commonMain/composeResources/files/140.gif b/shared/src/commonMain/composeResources/files/140.gif new file mode 100644 index 0000000..41281c8 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/140.gif differ diff --git a/shared/src/commonMain/composeResources/files/141.gif b/shared/src/commonMain/composeResources/files/141.gif new file mode 100644 index 0000000..7fb885f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/141.gif differ diff --git a/shared/src/commonMain/composeResources/files/142.gif b/shared/src/commonMain/composeResources/files/142.gif new file mode 100644 index 0000000..12ffa92 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/142.gif differ diff --git a/shared/src/commonMain/composeResources/files/143.gif b/shared/src/commonMain/composeResources/files/143.gif new file mode 100644 index 0000000..6e2f646 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/143.gif differ diff --git a/shared/src/commonMain/composeResources/files/144.gif b/shared/src/commonMain/composeResources/files/144.gif new file mode 100644 index 0000000..26f6399 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/144.gif differ diff --git a/shared/src/commonMain/composeResources/files/145.gif b/shared/src/commonMain/composeResources/files/145.gif new file mode 100644 index 0000000..92693fe Binary files /dev/null and b/shared/src/commonMain/composeResources/files/145.gif differ diff --git a/shared/src/commonMain/composeResources/files/146.gif b/shared/src/commonMain/composeResources/files/146.gif new file mode 100644 index 0000000..30cec38 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/146.gif differ diff --git a/shared/src/commonMain/composeResources/files/147.gif b/shared/src/commonMain/composeResources/files/147.gif new file mode 100644 index 0000000..edfc377 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/147.gif differ diff --git a/shared/src/commonMain/composeResources/files/148.gif b/shared/src/commonMain/composeResources/files/148.gif new file mode 100644 index 0000000..5c19667 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/148.gif differ diff --git a/shared/src/commonMain/composeResources/files/149.gif b/shared/src/commonMain/composeResources/files/149.gif new file mode 100644 index 0000000..588d817 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/149.gif differ diff --git a/shared/src/commonMain/composeResources/files/150.gif b/shared/src/commonMain/composeResources/files/150.gif new file mode 100644 index 0000000..a5ee13f Binary files /dev/null and b/shared/src/commonMain/composeResources/files/150.gif differ diff --git a/shared/src/commonMain/composeResources/files/151.gif b/shared/src/commonMain/composeResources/files/151.gif new file mode 100644 index 0000000..0b0101b Binary files /dev/null and b/shared/src/commonMain/composeResources/files/151.gif differ diff --git a/shared/src/commonMain/composeResources/files/152.gif b/shared/src/commonMain/composeResources/files/152.gif new file mode 100644 index 0000000..c6453a2 Binary files /dev/null and b/shared/src/commonMain/composeResources/files/152.gif differ diff --git a/shared/src/commonMain/composeResources/files/app_icon.png b/shared/src/commonMain/composeResources/files/app_icon.png new file mode 100644 index 0000000..53fc536 Binary files /dev/null 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 2d87d1a..68a8521 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/App.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/App.kt @@ -1,22 +1,110 @@ package plus.rua.project +import androidx.compose.animation.AnimatedContent +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 +import androidx.compose.animation.togetherWith import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.tooling.preview.Preview +import plus.rua.project.ui.AboutScreen import plus.rua.project.ui.CalendarMonthView +import plus.rua.project.ui.LicensesScreen + +private enum class Screen { Main, About, Licenses } /** - * 应用入口 Composable,根据系统主题切换明暗 ColorScheme 并包裹 CalendarMonthView。 + * 应用入口 Composable,根据系统主题切换明暗 ColorScheme 并管理页面导航。 */ @Composable @Preview(name = "Calendar App") fun App() { + var currentScreen by remember { mutableStateOf(Screen.Main) } + var backProgress by remember { mutableFloatStateOf(0f) } + + val handleBack: () -> Unit = { + backProgress = 0f + when (currentScreen) { + Screen.About -> currentScreen = Screen.Main + Screen.Licenses -> currentScreen = Screen.About + else -> {} + } + } + + val handleCancel: () -> Unit = { + backProgress = 0f + } + val colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme() MaterialTheme(colorScheme = colorScheme) { - CalendarMonthView(modifier = Modifier) + AnimatedContent( + targetState = currentScreen, + transitionSpec = { + if (targetState.ordinal > initialState.ordinal) { + // 正向导航:新页面从右侧滑入覆盖,旧页面略微左移+淡出 + (slideInHorizontally { it } + fadeIn()) togetherWith + (slideOutHorizontally { -it / 4 } + fadeOut()) + } else { + // 返回导航:新页面从左侧滑入,旧页面向右侧滑出 + (slideInHorizontally(animationSpec = tween(250)) { -it } + fadeIn(animationSpec = tween(250))) togetherWith + (slideOutHorizontally(animationSpec = tween(250)) { it } + fadeOut(animationSpec = tween(250))) + } + }, + modifier = Modifier.fillMaxSize() + ) { screen -> + when (screen) { + Screen.Main -> CalendarMonthView( + modifier = Modifier, + onNavigateToAbout = { currentScreen = Screen.About } + ) + Screen.About -> { + PredictiveBackHandler( + enabled = backProgress == 0f, + onProgress = { backProgress = it }, + onBack = handleBack, + onCancel = handleCancel + ) + AboutScreen( + onBack = { currentScreen = Screen.Main }, + onNavigateToLicenses = { currentScreen = Screen.Licenses }, + modifier = Modifier.graphicsLayer { + translationX = backProgress * size.width * 0.3f + scaleX = 1f - backProgress * 0.05f + scaleY = 1f - backProgress * 0.05f + } + ) + } + Screen.Licenses -> { + PredictiveBackHandler( + enabled = backProgress == 0f, + onProgress = { backProgress = it }, + onBack = handleBack, + onCancel = handleCancel + ) + LicensesScreen( + onBack = { currentScreen = Screen.About }, + modifier = Modifier.graphicsLayer { + translationX = backProgress * size.width * 0.3f + scaleX = 1f - backProgress * 0.05f + scaleY = 1f - backProgress * 0.05f + } + ) + } + } + } } } diff --git a/shared/src/commonMain/kotlin/plus/rua/project/AppInfo.kt b/shared/src/commonMain/kotlin/plus/rua/project/AppInfo.kt new file mode 100644 index 0000000..09f6e9f --- /dev/null +++ b/shared/src/commonMain/kotlin/plus/rua/project/AppInfo.kt @@ -0,0 +1,9 @@ +package plus.rua.project + +/** + * 应用常量信息。 + */ +object AppInfo { + const val NAME = "鸭鸭日历" + const val VERSION = "1.0" +} diff --git a/shared/src/commonMain/kotlin/plus/rua/project/Platform.kt b/shared/src/commonMain/kotlin/plus/rua/project/Platform.kt index 80a737e..116f9d3 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/Platform.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/Platform.kt @@ -1,7 +1,35 @@ package plus.rua.project +import androidx.compose.runtime.Composable + interface Platform { val name: String } -expect fun getPlatform(): Platform \ No newline at end of file +expect fun getPlatform(): Platform + +/** + * 获取 GIF 资源的 URI。 + * + * @param gifFile GIF 文件名(如 "001.gif") + * @return 平台特定的资源 URI + */ +expect fun getGifUri(gifFile: String): String + +expect fun getAppIconUri(): String + +/** + * 预测性返回手势处理器(Android 13+)。 + * + * @param enabled 是否启用 + * @param onProgress 手势进度回调(0.0~1.0),跟手过程中持续调用 + * @param onBack 手势完成回调(滑动距离足够,执行返回) + * @param onCancel 手势取消回调(滑动距离不足,回弹) + */ +@Composable +expect fun PredictiveBackHandler( + enabled: Boolean = true, + onProgress: (Float) -> Unit = {}, + onBack: () -> Unit, + onCancel: () -> Unit = {} +) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/AboutScreen.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/AboutScreen.kt new file mode 100644 index 0000000..4d25a0b --- /dev/null +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/AboutScreen.kt @@ -0,0 +1,114 @@ +package plus.rua.project.ui + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.unit.dp +import com.github.panpf.sketch.AsyncImage +import plus.rua.project.AppInfo +import plus.rua.project.getAppIconUri + +/** + * 关于页面,展示应用图标、名称、版本号及开源许可入口。 + * + * @param onBack 返回回调 + * @param onNavigateToLicenses 跳转到开源许可页面回调 + * @param modifier 布局修饰符 + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AboutScreen( + onBack: () -> Unit, + onNavigateToLicenses: () -> Unit, + modifier: Modifier = Modifier +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("关于鸭鸭日历") }, + navigationIcon = { + IconButton(onClick = onBack) { + Canvas(modifier = Modifier.size(24.dp)) { + val strokeWidth = 2.dp.toPx() + drawLine( + color = Color.White, + start = Offset(size.width * 0.75f, size.height * 0.25f), + end = Offset(size.width * 0.25f, size.height * 0.5f), + strokeWidth = strokeWidth, + cap = StrokeCap.Round + ) + drawLine( + color = Color.White, + start = Offset(size.width * 0.25f, size.height * 0.5f), + end = Offset(size.width * 0.75f, size.height * 0.75f), + strokeWidth = strokeWidth, + cap = StrokeCap.Round + ) + } + } + } + ) + }, + modifier = modifier + ) { innerPadding -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .padding(horizontal = 24.dp) + ) { + Spacer(modifier = Modifier.height(48.dp)) + + val appIconUri = remember { getAppIconUri() } + AsyncImage( + uri = appIconUri, + contentDescription = "应用图标", + modifier = Modifier + .size(80.dp) + .clip(RoundedCornerShape(16.dp)) + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = AppInfo.NAME, + style = MaterialTheme.typography.titleLarge + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "版本:${AppInfo.VERSION}", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Spacer(modifier = Modifier.height(48.dp)) + + TextButton(onClick = onNavigateToLicenses) { + Text("开放源代码许可") + } + } + } +} diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/AnimatedGif.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/AnimatedGif.kt index 9270b9c..a331dff 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/AnimatedGif.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/AnimatedGif.kt @@ -1,23 +1,60 @@ package plus.rua.project.ui +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.graphicsLayer import com.github.panpf.sketch.AsyncImage +import plus.rua.project.getGifUri /** - * 显示动画 GIF 图片。 + * GIF 文件名列表(001.gif ~ 152.gif)。 + */ +private val GIF_FILES = (1..152).map { "${it.toString().padStart(3, '0')}.gif" } + +/** + * 显示动画 GIF 图片,切换日期时随机选择一个。 * * @param modifier 应用于图片的 Modifier * @param contentDescription 无障碍描述 + * @param seed 用于控制重新随机时机的 key,变化时重新选择 GIF */ @Composable fun AnimatedGif( modifier: Modifier = Modifier, contentDescription: String? = null, + seed: Any? = null, ) { + val gifFile = remember(seed) { GIF_FILES.random() } + val uri = remember(gifFile) { getGifUri(gifFile) } + val scale = remember { Animatable(0f) } + + LaunchedEffect(seed) { + scale.snapTo(0f) + scale.animateTo( + targetValue = 1.1f, + animationSpec = tween(250, easing = FastOutSlowInEasing), + ) + scale.animateTo( + targetValue = 1f, + animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy), + ) + } + AsyncImage( - uri = "compose.resource://files/puppy_1.gif", + uri = uri, contentDescription = contentDescription, - modifier = modifier, + modifier = modifier + .graphicsLayer { + scaleX = scale.value + scaleY = scale.value + alpha = scale.value.coerceIn(0f, 1f) + }, ) } diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt index 0a3d2b4..6f9a474 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/BottomCard.kt @@ -147,6 +147,15 @@ fun BottomCard( fontSize = 14.sp ) } + Spacer(modifier = Modifier.height(12.dp)) + AnimatedGif( + modifier = Modifier + .fillMaxWidth() + .height(140.dp) + .align(Alignment.CenterHorizontally), + contentDescription = "可爱小狗", + seed = selectedDate, + ) } } } diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt index bf31745..a43b1c6 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -1,6 +1,8 @@ package plus.rua.project.ui import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn @@ -28,6 +30,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -75,7 +78,8 @@ import kotlin.time.Clock */ @Composable fun CalendarMonthView( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onNavigateToAbout: () -> Unit = {} ) { val coroutineScope = rememberCoroutineScope() val viewModel = remember { CalendarViewModel(coroutineScope) } @@ -203,6 +207,7 @@ fun CalendarMonthView( Box( modifier = modifier .fillMaxSize() + .background(MaterialTheme.colorScheme.background) .statusBarsPadding() .onSizeChanged { size -> screenWidthPx = size.width @@ -402,11 +407,11 @@ fun CalendarMonthView( MenuIcon() } - // Scrim:菜单展开时覆盖全屏,点击关闭 + // Scrim:全透明,仅拦截点击关闭菜单,无动画 AnimatedVisibility( visible = isMenuExpanded, - enter = fadeIn(tween(300)), - exit = fadeOut(tween(200)) + enter = EnterTransition.None, + exit = ExitTransition.None ) { Box( modifier = Modifier @@ -414,7 +419,6 @@ fun CalendarMonthView( .pointerInput(Unit) { detectTapGestures { isMenuExpanded = false } } - .background(Color.Black.copy(alpha = 0.32f)) ) } @@ -457,6 +461,19 @@ fun CalendarMonthView( if (!viewModel.isYearView) viewModel.toggleYearView() } ) + HorizontalDivider( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + modifier = Modifier.padding(horizontal = 8.dp) + ) + MenuItem( + text = "关于", + selected = false, + onClick = { + isMenuExpanded = false + onNavigateToAbout() + } + ) } } } diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt index c6562bf..02f2b0d 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.tween import androidx.compose.animation.core.updateTransition +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box @@ -291,6 +292,7 @@ fun DayCell( modifier = Modifier .align(shiftAlignment) .zIndex(1f) + .background(MaterialTheme.colorScheme.background) .then(shiftPadding) .padding(horizontal = 2.dp) ) @@ -305,6 +307,7 @@ fun DayCell( modifier = Modifier .align(Alignment.TopEnd) .zIndex(1f) + .background(MaterialTheme.colorScheme.background) .padding(top = 1.dp, end = 2.dp) .padding(horizontal = 2.dp) ) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/Licenses.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/Licenses.kt new file mode 100644 index 0000000..980b09c --- /dev/null +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/Licenses.kt @@ -0,0 +1,27 @@ +package plus.rua.project.ui + +/** + * 许可证条目数据。 + * + * @param library 库名称 + * @param license 许可证名称 + */ +data class LicenseItem( + val library: String, + val license: String +) + +/** + * 项目使用的第三方库及其许可证列表。 + */ +val licenses = listOf( + LicenseItem("Kotlin", "Apache-2.0"), + LicenseItem("Compose Multiplatform", "Apache-2.0"), + LicenseItem("Material 3", "Apache-2.0"), + LicenseItem("kotlinx-datetime", "Apache-2.0"), + LicenseItem("tyme4kt", "MIT"), + LicenseItem("Sketch", "Apache-2.0"), + LicenseItem("AndroidX Activity", "Apache-2.0"), + LicenseItem("AndroidX Lifecycle", "Apache-2.0"), + LicenseItem("JUnit", "EPL-1.0"), +) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/LicensesScreen.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/LicensesScreen.kt new file mode 100644 index 0000000..84939a1 --- /dev/null +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/LicensesScreen.kt @@ -0,0 +1,98 @@ +package plus.rua.project.ui + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.unit.dp + +/** + * 开放源代码许可页面,展示项目使用的第三方库及其许可证。 + * + * @param onBack 返回回调 + * @param modifier 布局修饰符 + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LicensesScreen( + onBack: () -> Unit, + modifier: Modifier = Modifier +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("开放源代码许可") }, + navigationIcon = { + IconButton(onClick = onBack) { + Canvas(modifier = Modifier.size(24.dp)) { + val strokeWidth = 2.dp.toPx() + drawLine( + color = Color.White, + start = Offset(size.width * 0.75f, size.height * 0.25f), + end = Offset(size.width * 0.25f, size.height * 0.5f), + strokeWidth = strokeWidth, + cap = StrokeCap.Round + ) + drawLine( + color = Color.White, + start = Offset(size.width * 0.25f, size.height * 0.5f), + end = Offset(size.width * 0.75f, size.height * 0.75f), + strokeWidth = strokeWidth, + cap = StrokeCap.Round + ) + } + } + } + ) + }, + modifier = modifier + ) { innerPadding -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + ) { + items(licenses) { item -> + Column { + ListItem( + modifier = Modifier.clickable { }, + headlineContent = { + Text( + text = item.library, + style = MaterialTheme.typography.bodyLarge + ) + }, + trailingContent = { + Text( + text = item.license, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + ) + HorizontalDivider( + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colorScheme.outlineVariant + ) + } + } + } + } +} diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/MonthHeader.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/MonthHeader.kt index 5064371..7db15ff 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/MonthHeader.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/MonthHeader.kt @@ -65,6 +65,7 @@ fun MonthHeader( ) { (y, m) -> Text( text = "${y}年${m}月", + color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.titleLarge ) } @@ -84,6 +85,7 @@ fun MonthHeader( ) { week -> Text( text = "第${week}周", + color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.bodySmall ) } diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt index d4a129a..4ab39b4 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt @@ -128,8 +128,8 @@ fun YearGridView( // P0-H: 预测量星期标签 val weekdayLayouts = remember(textMeasurer, colors) { - WEEKDAY_LABELS.associate { label -> - label to textMeasurer.measure(label, TextStyle(fontSize = 8.sp, color = colors.weekday)) + WEEKDAY_LABELS.associateWith { label -> + textMeasurer.measure(label, TextStyle(fontSize = 8.sp, color = colors.weekday)) } } @@ -193,10 +193,11 @@ private fun MiniMonth( val density = LocalDensity.current val dayRowCount = days.size / 7 val titleHeightPx = with(density) { 14.sp.toPx() } + val titleToWeekdayGapPx = with(density) { 4.dp.toPx() } val weekdayHeightPx = with(density) { 12.sp.toPx() } val dayCellHeightPx = with(density) { (12.sp.toPx() + 4.dp.toPx()) } val totalHeight = with(density) { - (titleHeightPx + weekdayHeightPx + dayRowCount * dayCellHeightPx).toDp() + (titleHeightPx + titleToWeekdayGapPx + weekdayHeightPx + dayRowCount * dayCellHeightPx).toDp() } Column( @@ -220,7 +221,7 @@ private fun MiniMonth( ) // 2. 绘制星期行 - val weekdayY = titleHeightPx + val weekdayY = titleHeightPx + titleToWeekdayGapPx WEEKDAY_LABELS.forEachIndexed { i, label -> val layout = weekdayLayouts[label]!! drawText( @@ -233,7 +234,7 @@ private fun MiniMonth( } // 3. 绘制日期网格 - val dayGridY = titleHeightPx + weekdayHeightPx + val dayGridY = weekdayY + weekdayHeightPx days.forEachIndexed { index, dayData -> val row = index / 7 @@ -347,6 +348,7 @@ fun YearHeader( ) { y -> Text( text = "${y}年", + color = MaterialTheme.colorScheme.onBackground, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold ) diff --git a/shared/src/iosMain/kotlin/plus/rua/project/Platform.ios.kt b/shared/src/iosMain/kotlin/plus/rua/project/Platform.ios.kt index b8abfdb..683436e 100644 --- a/shared/src/iosMain/kotlin/plus/rua/project/Platform.ios.kt +++ b/shared/src/iosMain/kotlin/plus/rua/project/Platform.ios.kt @@ -1,5 +1,6 @@ package plus.rua.project +import androidx.compose.runtime.Composable import platform.UIKit.UIDevice class IOSPlatform : Platform { @@ -7,4 +8,18 @@ class IOSPlatform : Platform { UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion } -actual fun getPlatform(): Platform = IOSPlatform() \ No newline at end of file +actual fun getPlatform(): Platform = IOSPlatform() + +actual fun getGifUri(gifFile: String): String = "compose.resource://files/$gifFile" + +actual fun getAppIconUri(): String = "compose.resource://files/app_icon.png" + +@Composable +actual fun PredictiveBackHandler( + enabled: Boolean, + onProgress: (Float) -> Unit, + onBack: () -> Unit, + onCancel: () -> Unit +) { + // iOS 没有预测性返回手势,由导航栏按钮处理 +}