diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt
index b388107..aa2bbe0 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoNavigation.kt
@@ -12,6 +12,7 @@ import cn.super12138.todo.ui.pages.editor.TodoEditorPage
import cn.super12138.todo.ui.pages.main.MainPage
import cn.super12138.todo.ui.pages.settings.SettingsAbout
import cn.super12138.todo.ui.pages.settings.SettingsAboutLicence
+import cn.super12138.todo.ui.pages.settings.SettingsAboutSpecial
import cn.super12138.todo.ui.pages.settings.SettingsAppearance
import cn.super12138.todo.ui.pages.settings.SettingsData
import cn.super12138.todo.ui.pages.settings.SettingsInterface
@@ -123,8 +124,16 @@ fun TodoNavigation(
composable(TodoScreen.SettingsAbout.name) {
SettingsAbout(
+ toSpecialPage = { navController.navigate(TodoScreen.SettingsAboutSpecial.name) },
+ toLicencePage = { navController.navigate(TodoScreen.SettingsAboutLicence.name) },
onNavigateUp = { navController.navigateUp() },
- toLicencePage = { navController.navigate(TodoScreen.SettingsAboutLicence.name) }
+ )
+ }
+
+ composable(TodoScreen.SettingsAboutSpecial.name) {
+ SettingsAboutSpecial(
+ viewModel = viewModel,
+ onNavigateUp = { navController.navigateUp() }
)
}
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt
index fe5b0ee..a1e365a 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/navigation/TodoScreen.kt
@@ -8,5 +8,6 @@ enum class TodoScreen {
SettingsInterface,
SettingsData,
SettingsAbout,
+ SettingsAboutSpecial,
SettingsAboutLicence
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt
index 7cc6ed9..f68d510 100644
--- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAbout.kt
@@ -2,6 +2,7 @@ package cn.super12138.todo.ui.pages.settings
import android.content.Intent
import android.net.Uri
+import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@@ -14,6 +15,10 @@ import androidx.compose.material.icons.outlined.Person4
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@@ -28,8 +33,9 @@ import cn.super12138.todo.utils.SystemUtils
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsAbout(
- onNavigateUp: () -> Unit,
+ toSpecialPage: () -> Unit,
toLicencePage: () -> Unit,
+ onNavigateUp: () -> Unit,
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
@@ -46,10 +52,22 @@ fun SettingsAbout(
.padding(innerPadding)
.verticalScroll(rememberScrollState())
) {
+ var clickCount by remember { mutableIntStateOf(0) }
SettingsItem(
leadingIcon = Icons.Outlined.Numbers,
title = stringResource(R.string.pref_app_version),
- description = SystemUtils.getAppVersion(context)
+ description = SystemUtils.getAppVersion(context),
+ onClick = {
+ clickCount++
+ if (clickCount == 5) {
+ if ((System.currentTimeMillis() % 2) == 0.toLong()) {
+ toSpecialPage()
+ } else {
+ Toast.makeText(context, "✨", Toast.LENGTH_SHORT).show()
+ }
+ clickCount = 0
+ }
+ }
)
SettingsItem(
leadingIcon = Icons.Outlined.Person4,
diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutSpecial.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutSpecial.kt
new file mode 100644
index 0000000..99ed198
--- /dev/null
+++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutSpecial.kt
@@ -0,0 +1,234 @@
+package cn.super12138.todo.ui.pages.settings
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.scaleIn
+import androidx.compose.animation.scaleOut
+import androidx.compose.animation.shrinkVertically
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBars
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import cn.super12138.todo.R
+import cn.super12138.todo.ui.viewmodels.MainViewModel
+import cn.super12138.todo.utils.VibrationUtils
+
+@Composable
+fun SettingsAboutSpecial(
+ viewModel: MainViewModel,
+ onNavigateUp: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ val configuration = LocalConfiguration.current
+ val screenHeight = configuration.screenHeightDp.dp
+ var showBanner by rememberSaveable { mutableStateOf(false) }
+ Surface(color = SpecialDefaults.WallColor) {
+ Box(Modifier.fillMaxSize()) {
+ AnimatedVisibility(
+ visible = showBanner,
+ enter = expandVertically(),
+ exit = shrinkVertically()
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(screenHeight / 4)
+ .clip(MaterialTheme.shapes.large)
+ .background(SpecialDefaults.BannerColor)
+ .padding(WindowInsets.statusBars.asPaddingValues())
+
+ ) {
+ Text(
+ text = stringResource(R.string.happy_birthday),
+ style = MaterialTheme.typography.headlineMedium
+ )
+ }
+ }
+ // 蛋糕
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier
+ .align(Alignment.BottomCenter)
+ ) {
+ Cake(
+ onCandleClick = {
+ showBanner = it
+ if (it) viewModel.playConfetti()
+ }
+ )
+ // 桌子
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .height(SpecialDefaults.TableHeight)
+ .background(SpecialDefaults.TableColor)
+ )
+ }
+ }
+ }
+}
+
+
+@Composable
+private fun Cake(
+ onCandleClick: (Boolean) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = modifier
+ ) {
+ val configuration = LocalConfiguration.current
+ val screenWidth = configuration.screenWidthDp.dp
+ Row {
+ Candle(onClick = onCandleClick)
+ }
+ CakePiece(
+ width = screenWidth / 4,
+ height = SpecialDefaults.Cake2Height,
+ color = SpecialDefaults.Cake2Color
+ )
+ CakePiece(
+ width = screenWidth / 3,
+ height = SpecialDefaults.Cake1Height,
+ color = SpecialDefaults.Cake1Color
+ )
+ }
+}
+
+@Composable
+private fun CakePiece(
+ width: Dp,
+ height: Dp,
+ color: Color,
+ modifier: Modifier = Modifier
+) {
+ Box(
+ modifier
+ .width(width)
+ .height(height)
+ .clip(
+ MaterialTheme.shapes.small.copy(
+ bottomStart = CornerSize(0.dp),
+ bottomEnd = CornerSize(0.dp)
+ )
+ )
+ .background(color)
+ )
+}
+
+@Composable
+fun Candle(
+ onClick: (Boolean) -> Unit = {},
+ modifier: Modifier = Modifier
+) {
+ var showFlame by rememberSaveable { mutableStateOf(false) }
+ val interactionSource = remember { MutableInteractionSource() }
+ val view = LocalView.current
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = modifier.clickable(
+ interactionSource = interactionSource,
+ indication = null,
+ onClick = {
+ showFlame = !showFlame
+ VibrationUtils.performHapticFeedback(view)
+ onClick(showFlame)
+ }
+ )
+ ) {
+ // 火苗
+ Box {
+ androidx.compose.animation.AnimatedVisibility(
+ visible = showFlame,
+ enter = scaleIn(),
+ exit = scaleOut(),
+ modifier = Modifier
+ .align(Alignment.Center)
+ .padding(bottom = 2.5.dp)
+ ) {
+ Box(
+ Modifier
+ .size(10.dp)
+ .background(
+ brush = Brush.radialGradient(
+ listOf(
+ SpecialDefaults.FlameCenterColor,
+ SpecialDefaults.FlameAroundColor,
+ Color.Transparent
+ )
+ )
+ )
+ )
+ }
+ Box(
+ Modifier
+ .width(SpecialDefaults.WickWidth)
+ .height(SpecialDefaults.WickHeight)
+ .background(SpecialDefaults.WickColor)
+ .align(Alignment.BottomCenter)
+ )
+ }
+ Box(
+ Modifier
+ .width(SpecialDefaults.CandleBodyWidth)
+ .height(SpecialDefaults.CandleBodyHeight)
+ .background(Color(0xFFECCE48))
+ )
+ }
+}
+
+private object SpecialDefaults {
+ // Color
+ val WallColor = Color(0xFFFFFBEA)
+ val TableColor = Color(0xFF412200)
+ val Cake1Color = Color(0xFFFFE9A0)
+ val Cake2Color = Color(0xFFFFECBF)
+ val FlameCenterColor = Color(0xFFFF5100)
+ val FlameAroundColor = Color(0xFFF82323)
+ val WickColor = Color(0xFF1E1E1E)
+ val BannerColor = Color(0xFFFFF1BC)
+
+ // Size
+ val TableHeight = 30.dp
+ val Cake1Height = 60.dp
+ val Cake2Height = 45.dp
+ val CandleBodyWidth = 10.dp
+ val CandleBodyHeight = 20.dp
+ val WickWidth = 1.5.dp
+ val WickHeight = 8.dp
+}
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index cfcaf6d..38d99a4 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -107,4 +107,5 @@
安全模式
阻止截屏并保护后台预览图
更多
+ 待办1岁生日快乐
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 038d86d..967c6c6 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -108,4 +108,5 @@
Secure Mode
Prevent screenshots and protect the background preview image
More
+ Happy 1st birthday to ToDo
\ No newline at end of file