diff --git a/Pink/app/src/main/AndroidManifest.xml b/Pink/app/src/main/AndroidManifest.xml index 2b5cca3..b733610 100644 --- a/Pink/app/src/main/AndroidManifest.xml +++ b/Pink/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ android:name=".MainActivity" android:exported="true" android:label="@string/app_name" + android:screenOrientation="portrait" android:theme="@style/Theme.PinkOV"> diff --git a/Pink/app/src/main/java/com/pinkbear/pinkov/MainActivity.kt b/Pink/app/src/main/java/com/pinkbear/pinkov/MainActivity.kt index 86a2e79..b295b2d 100644 --- a/Pink/app/src/main/java/com/pinkbear/pinkov/MainActivity.kt +++ b/Pink/app/src/main/java/com/pinkbear/pinkov/MainActivity.kt @@ -4,7 +4,9 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold import androidx.compose.runtime.Composable @@ -12,11 +14,15 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp import androidx.compose.ui.tooling.preview.PreviewScreenSizes import com.pinkbear.pinkov.navigation.AppDestinations import com.pinkbear.pinkov.ui.page.HomePage import com.pinkbear.pinkov.ui.page.MinePage +import com.pinkbear.pinkov.ui.page.RecordsPage import com.pinkbear.pinkov.ui.page.ScanPage import com.pinkbear.pinkov.ui.theme.PinkOVTheme @@ -48,11 +54,20 @@ fun PinkOVApp() { item( icon = { Icon( - painterResource(it.icon), - contentDescription = it.label + modifier = Modifier.size(22.dp), + painter = painterResource(it.icon), + contentDescription = it.label, + tint = if (it == currentDestination) MaterialTheme.colorScheme.primary + else MaterialTheme.colorScheme.onSurfaceVariant + ) + }, + label = { + Text( + it.label, + color = if (it == currentDestination) MaterialTheme.colorScheme.primary + else MaterialTheme.colorScheme.onSurfaceVariant ) }, - label = { Text(it.label) }, selected = it == currentDestination, onClick = { currentDestination = it } ) @@ -63,6 +78,7 @@ fun PinkOVApp() { AppDestinations.HOME -> HomePage( onScanClick = { showScanPage = true } ) + AppDestinations.RECORDS -> RecordsPage() AppDestinations.MINE -> MinePage() } } diff --git a/Pink/app/src/main/java/com/pinkbear/pinkov/navigation/AppDestinations.kt b/Pink/app/src/main/java/com/pinkbear/pinkov/navigation/AppDestinations.kt index d96e1fd..ce905ad 100644 --- a/Pink/app/src/main/java/com/pinkbear/pinkov/navigation/AppDestinations.kt +++ b/Pink/app/src/main/java/com/pinkbear/pinkov/navigation/AppDestinations.kt @@ -10,5 +10,6 @@ enum class AppDestinations( val icon: Int, ) { HOME("Home", R.drawable.ic_home), + RECORDS("Records", R.drawable.ic_records), MINE("Mine", R.drawable.ic_mine), } \ No newline at end of file diff --git a/Pink/app/src/main/java/com/pinkbear/pinkov/ui/page/HomePage.kt b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/page/HomePage.kt index 27fc7fb..26c3db1 100644 --- a/Pink/app/src/main/java/com/pinkbear/pinkov/ui/page/HomePage.kt +++ b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/page/HomePage.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale @@ -62,47 +63,77 @@ fun HomePage( Box( modifier = Modifier .fillMaxWidth() - .fillMaxHeight(1f / 3f), - contentAlignment = Alignment.Center + .fillMaxHeight(1f / 3f) ) { - // Breathing animation for the scan button - val infiniteTransition = rememberInfiniteTransition() - val breathScale by infiniteTransition.animateFloat( - initialValue = 0.88f, - targetValue = 1f, - animationSpec = infiniteRepeatable( - animation = tween(2000, easing = FastOutLinearInEasing), - repeatMode = RepeatMode.Reverse - ), - label = "breath" - ) - + // Scan button (centered) Box( - modifier = Modifier - .size(150.dp) - .scale(breathScale) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary) - .clickable { onScanClick() }, + modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - Text( - text = "扫描", - color = MaterialTheme.colorScheme.onPrimary, - fontSize = 18.sp, - fontWeight = FontWeight.Bold + // Breathing animation for the scan button + val infiniteTransition = rememberInfiniteTransition() + val breathScale by infiniteTransition.animateFloat( + initialValue = 0.88f, + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = tween(2000, easing = FastOutLinearInEasing), + repeatMode = RepeatMode.Reverse + ), + label = "breath" ) - } - } - HorizontalDivider(thickness = 1.dp, color = MaterialTheme.colorScheme.outlineVariant) + Box( + modifier = Modifier + .size(150.dp) + .scale(breathScale) + .background( + brush = Brush.radialGradient( + colors = listOf( + Color(0xFFFFA3D3), // center: RGB(255, 163, 211) + Color(0xFFFF8BBC), // edge: RGB(255, 139, 188) + ) + ), + shape = CircleShape + ) + .clip(CircleShape) + .clickable { onScanClick() }, + contentAlignment = Alignment.Center + ) { + Text( + text = "扫描试纸", + color = MaterialTheme.colorScheme.onPrimary, + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + } + } + + // Bottom-only shadow gradient + Box( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .height(8.dp) + .background( + brush = Brush.verticalGradient( + colors = listOf( + Color.Transparent, + Color.Black.copy(alpha = 0.03f), + Color.Black.copy(alpha = 0.08f), + Color.Black.copy(alpha = 0.14f), + ) + ) + ) + ) + } // ---- Bottom 2/3: Result list ---- if (records.isEmpty()) { Box( modifier = Modifier .fillMaxWidth() - .fillMaxHeight(), + .fillMaxHeight() + .background(MaterialTheme.colorScheme.surfaceVariant), contentAlignment = Alignment.Center ) { Text( @@ -116,8 +147,10 @@ fun HomePage( modifier = Modifier .fillMaxWidth() .fillMaxHeight() + .background(MaterialTheme.colorScheme.surfaceVariant) ) { item { + Spacer(modifier = Modifier.height(12.dp)) // Header row RecordHeaderRow() } @@ -126,10 +159,7 @@ fun HomePage( record = record, onLongPress = { recordToDelete = record } ) - HorizontalDivider( - thickness = 0.5.dp, - color = MaterialTheme.colorScheme.outlineVariant - ) + Spacer(modifier = Modifier.height(8.dp)) } } } @@ -169,8 +199,8 @@ private fun RecordHeaderRow() { Row( modifier = Modifier .fillMaxWidth() - .background(MaterialTheme.colorScheme.surface) - .padding(vertical = 8.dp, horizontal = 4.dp), + .background(MaterialTheme.colorScheme.surfaceVariant) + .padding(vertical = 8.dp, horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically ) { Text( @@ -209,7 +239,8 @@ private fun RecordHeaderRow() { } /** - * A single record row. Image column takes ~40% of screen width. + * A single record card with rounded corners and subtle shadow. + * Image column takes ~40% of card width. */ @OptIn(ExperimentalFoundationApi::class) @Composable @@ -236,68 +267,76 @@ private fun RecordRow( else -> Color.White } - Row( + Card( modifier = Modifier .fillMaxWidth() + .padding(horizontal = 16.dp) .combinedClickable( onClick = { /* Click could open detail view in future */ }, onLongClick = onLongPress - ) - .background(MaterialTheme.colorScheme.surface) - .padding(vertical = 6.dp, horizontal = 4.dp), - verticalAlignment = Alignment.CenterVertically + ), + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface) ) { - // Result value - Text( - text = record.resultValue, - modifier = Modifier.weight(0.8f), - color = resultColor, - fontSize = 16.sp, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Center - ) - // Chinese name - Text( - text = record.resultName, - modifier = Modifier.weight(1.0f), - color = resultColor, - fontSize = 14.sp, - fontWeight = FontWeight.Medium, - textAlign = TextAlign.Center - ) - // Time - Text( - text = timeStr, - modifier = Modifier.weight(2.0f), - color = MaterialTheme.colorScheme.onSurfaceVariant, - fontSize = 11.sp, - textAlign = TextAlign.Center, - lineHeight = 14.sp, - overflow = TextOverflow.Ellipsis - ) - // Image thumbnail (40% = 2.0 of total 5.8 weight) - Box( + Row( modifier = Modifier - .weight(2.0f) .fillMaxWidth() - .aspectRatio(4f / 3f) - .clip(RoundedCornerShape(4.dp)) - .background(MaterialTheme.colorScheme.surface), - contentAlignment = Alignment.Center + .padding(vertical = 8.dp, horizontal = 8.dp), + verticalAlignment = Alignment.CenterVertically ) { - if (thumbnail != null) { - Image( - bitmap = thumbnail.asImageBitmap(), - contentDescription = "试纸图片", - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Fit - ) - } else { - Text( - text = "无图片", - color = MaterialTheme.colorScheme.onSurfaceVariant, - fontSize = 10.sp - ) + // Result value + Text( + text = record.resultValue, + modifier = Modifier.weight(0.8f), + color = resultColor, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + // Chinese name + Text( + text = record.resultName, + modifier = Modifier.weight(1.0f), + color = resultColor, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + textAlign = TextAlign.Center + ) + // Time + Text( + text = timeStr, + modifier = Modifier.weight(2.0f), + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 11.sp, + textAlign = TextAlign.Center, + lineHeight = 14.sp, + overflow = TextOverflow.Ellipsis + ) + // Image thumbnail (40% = 2.0 of total 5.8 weight) + Box( + modifier = Modifier + .weight(2.0f) + .fillMaxWidth() + .aspectRatio(4f / 3f) + .clip(RoundedCornerShape(4.dp)) + .background(MaterialTheme.colorScheme.surface), + contentAlignment = Alignment.Center + ) { + if (thumbnail != null) { + Image( + bitmap = thumbnail.asImageBitmap(), + contentDescription = "试纸图片", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Fit + ) + } else { + Text( + text = "无图片", + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 10.sp + ) + } } } } diff --git a/Pink/app/src/main/java/com/pinkbear/pinkov/ui/page/RecordsPage.kt b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/page/RecordsPage.kt new file mode 100644 index 0000000..0df2af1 --- /dev/null +++ b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/page/RecordsPage.kt @@ -0,0 +1,47 @@ +package com.pinkbear.pinkov.ui.page + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.PreviewScreenSizes +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.pinkbear.pinkov.ui.theme.PinkOVTheme + +@Composable +fun RecordsPage(modifier: Modifier = Modifier) { + Scaffold(modifier = modifier) { innerPadding -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + text = "经期记录", + color = MaterialTheme.colorScheme.onSurface, + fontSize = 18.sp + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "日历与经期记录功能即将上线", + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 14.sp, + textAlign = TextAlign.Center + ) + } + } + } +} + +@PreviewScreenSizes +@Composable +private fun RecordsPagePreview() { + PinkOVTheme { + RecordsPage() + } +} diff --git a/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Color.kt b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Color.kt index 0092eae..dc89203 100644 --- a/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Color.kt +++ b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Color.kt @@ -2,10 +2,12 @@ package com.pinkbear.pinkov.ui.theme import androidx.compose.ui.graphics.Color -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) +// Pink palette — dark theme +val Pink80 = Color(0xFFFF8BBC) // primary: RGB(255, 139, 188) +val PinkGrey80 = Color(0xFFFFA3D3) // secondary: RGB(255, 163, 211) +val Rose80 = Color(0xFFFFC1D4) // tertiary -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file +// Pink palette — light theme +val Pink40 = Color(0xFFFF8BBC) // primary: RGB(255, 139, 188) +val PinkGrey40 = Color(0xFFEC80A4) // secondary +val Rose40 = Color(0xFFFFA3D3) // tertiary: RGB(255, 163, 211) \ No newline at end of file diff --git a/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Theme.kt b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Theme.kt index 9cccdf8..382237d 100644 --- a/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Theme.kt +++ b/Pink/app/src/main/java/com/pinkbear/pinkov/ui/theme/Theme.kt @@ -12,15 +12,15 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 + primary = Pink80, + secondary = PinkGrey80, + tertiary = Rose80 ) private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 + primary = Pink40, + secondary = PinkGrey40, + tertiary = Rose40 /* Other default colors to override background = Color(0xFFFFFBFE), @@ -37,7 +37,7 @@ private val LightColorScheme = lightColorScheme( fun PinkOVTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, + dynamicColor: Boolean = false, content: @Composable () -> Unit ) { val colorScheme = when { diff --git a/Pink/app/src/main/res/drawable/ic_records.xml b/Pink/app/src/main/res/drawable/ic_records.xml new file mode 100644 index 0000000..9c40d79 --- /dev/null +++ b/Pink/app/src/main/res/drawable/ic_records.xml @@ -0,0 +1,9 @@ + + +