diff --git a/androidApp/src/main/assets/app_icon.png b/androidApp/src/main/assets/app_icon.png new file mode 100644 index 0000000..53fc536 Binary files /dev/null and b/androidApp/src/main/assets/app_icon.png differ diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 11845e1..9ad1068 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -4,5 +4,9 @@ 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 950aeab..e0ddf8b 100644 --- a/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt +++ b/shared/src/androidMain/kotlin/plus/rua/project/Platform.android.kt @@ -8,4 +8,6 @@ class AndroidPlatform : Platform { actual fun getPlatform(): Platform = AndroidPlatform() -actual fun getGifUri(gifFile: String): String = "file:///android_asset/gifs/$gifFile" \ No newline at end of file +actual fun getGifUri(gifFile: String): String = "file:///android_asset/gifs/$gifFile" + +actual fun getAppIconUri(): String = "file:///android_asset/app_icon.png" \ No newline at end of file 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..abc6d21 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/App.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/App.kt @@ -5,18 +5,40 @@ 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.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier 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) } + val colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme() MaterialTheme(colorScheme = colorScheme) { - CalendarMonthView(modifier = Modifier) + when (currentScreen) { + Screen.Main -> CalendarMonthView( + modifier = Modifier, + onNavigateToAbout = { currentScreen = Screen.About } + ) + Screen.About -> AboutScreen( + onBack = { currentScreen = Screen.Main }, + onNavigateToLicenses = { currentScreen = Screen.Licenses } + ) + Screen.Licenses -> LicensesScreen( + onBack = { currentScreen = Screen.About } + ) + } } } 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 9aa328a..d76804f 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/Platform.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/Platform.kt @@ -12,4 +12,6 @@ expect fun getPlatform(): Platform * @param gifFile GIF 文件名(如 "001.gif") * @return 平台特定的资源 URI */ -expect fun getGifUri(gifFile: String): String \ No newline at end of file +expect fun getGifUri(gifFile: String): String + +expect fun getAppIconUri(): String \ No newline at end of file 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/CalendarMonthView.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt index 6dbe170..a43b1c6 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -78,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) } @@ -468,7 +469,10 @@ fun CalendarMonthView( MenuItem( text = "关于", selected = false, - onClick = { /* TODO: 后续接入设置页 */ } + onClick = { + isMenuExpanded = false + onNavigateToAbout() + } ) } } 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/iosMain/kotlin/plus/rua/project/Platform.ios.kt b/shared/src/iosMain/kotlin/plus/rua/project/Platform.ios.kt index 0606b43..09db9f7 100644 --- a/shared/src/iosMain/kotlin/plus/rua/project/Platform.ios.kt +++ b/shared/src/iosMain/kotlin/plus/rua/project/Platform.ios.kt @@ -9,4 +9,6 @@ class IOSPlatform : Platform { actual fun getPlatform(): Platform = IOSPlatform() -actual fun getGifUri(gifFile: String): String = "compose.resource://files/$gifFile" \ No newline at end of file +actual fun getGifUri(gifFile: String): String = "compose.resource://files/$gifFile" + +actual fun getAppIconUri(): String = "compose.resource://files/app_icon.png" \ No newline at end of file