869 lines
33 KiB
Kotlin

package com.example.ppb_kelompok2
// Yosep
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.*
import com.example.ppb_kelompok2.ui.theme.PPB_Kelompok2Theme
import kotlinx.coroutines.delay
import kotlin.math.roundToInt
import kotlin.random.Random
// --- Main Activity ---
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
PPB_Kelompok2Theme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
AppNavigationGraph()
}
}
}
}
}
// --- Navigation Graph ---
@Composable
fun AppNavigationGraph() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "login") {
composable("login") { LoginScreen(navController = navController) }
composable("main") { MainAppScreen() }
}
}
// --- Login Screen ---
@Composable
fun LoginScreen(navController: NavController) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("MindTrack AI", style = MaterialTheme.typography.headlineLarge, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(8.dp))
Text(
"Lacak kesehatan mental Anda dengan kekuatan AI.",
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 32.dp)
)
Spacer(modifier = Modifier.height(32.dp))
Button(
onClick = { navController.navigate("main") {
popUpTo("login") { inclusive = true }
} },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 32.dp)
) {
Icon(Icons.Default.AccountCircle, contentDescription = "Google Icon") // Placeholder for Google Icon
Spacer(modifier = Modifier.width(8.dp))
Text("Masuk dengan Google")
}
}
}
// --- Main App Structure (with Bottom Navigation) ---
sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
object Journal : Screen("journal", "Jurnal", Icons.Default.Book)
object Assessment : Screen("assessment", "Penilaian", Icons.Default.Checklist)
object CognitiveTest : Screen("cognitive_test", "Tes Kognitif", Icons.Default.SportsEsports)
object History : Screen("history", "Riwayat & Grafik", Icons.Default.BarChart)
}
val bottomNavItems = listOf(
Screen.Journal,
Screen.Assessment,
Screen.CognitiveTest,
Screen.History
)
@Composable
fun MainAppScreen() {
val navController = rememberNavController()
Scaffold(
bottomBar = { AppBottomNavigation(navController = navController) }
) { innerPadding ->
AppNavHost(navController = navController, modifier = Modifier.padding(innerPadding))
}
}
@Composable
fun AppBottomNavigation(navController: NavHostController) {
NavigationBar {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
bottomNavItems.forEach { screen ->
NavigationBarItem(
icon = { Icon(screen.icon, contentDescription = screen.label) },
label = { Text(screen.label) },
selected = currentRoute == screen.route,
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.startDestinationId) { saveState = true }
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
@Composable
fun AppNavHost(navController: NavHostController, modifier: Modifier = Modifier) {
NavHost(
navController = navController,
startDestination = Screen.Journal.route,
modifier = modifier.fillMaxSize()
) {
composable(Screen.Journal.route) { JournalScreen() }
composable(Screen.Assessment.route) { AssessmentScreen() }
composable(Screen.CognitiveTest.route) { CognitiveTestScreen(navController) }
composable(Screen.History.route) { HistoryScreen() }
composable("memory_test") { MemoryTestScreen(navController) }
composable("focus_test") { FocusTestScreen(navController) }
composable("reaction_test") { ReactionSpeedTestScreen(navController) }
}
}
// --- App Screens ---
@Composable
fun JournalScreen() {
var journalText by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Card(modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Analisis AI", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(8.dp))
Text("Sentimen: Netral")
Text("Emosi Terdeteksi: Tenang")
}
}
OutlinedTextField(
value = journalText,
onValueChange = { journalText = it },
label = { Text("Tuliskan perasaanmu di sini...") },
modifier = Modifier
.fillMaxWidth()
.weight(1f)
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = { /* TODO: Implement save logic */ },
modifier = Modifier.fillMaxWidth()
) {
Text("Simpan Jurnal")
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AssessmentScreen() {
val indicators = remember {
listOf(
"Mood Sedih", "Rasa Bersalah", "Menarik Diri Sosial",
"Sulit Konsentrasi", "Kelelahan", "Pikiran Bunuh Diri"
)
}
val sliderValues = remember {
mutableStateMapOf<String, Float>().apply {
indicators.forEach { indicator ->
put(indicator, 0f)
}
}
}
val totalScore = sliderValues.values.sum().toInt()
val assessmentLevel = when (totalScore) {
in 0..4 -> "Normal"
in 5..9 -> "Ringan"
in 10..14 -> "Sedang"
else -> "Berat"
}
Scaffold(
topBar = {
TopAppBar(title = { Text("Penilaian Harian") })
}
) { innerPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.Start
) {
Text("Ringkasan Skor", style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
Text("Total Skor: $totalScore", style = MaterialTheme.typography.headlineMedium)
Text("Tingkat: $assessmentLevel", style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
}
}
}
items(indicators) { indicatorName ->
IndicatorItem(
indicatorName = indicatorName,
value = sliderValues[indicatorName] ?: 0f,
onValueChange = {
sliderValues[indicatorName] = it.roundToInt().toFloat()
}
)
}
item {
Button(
onClick = { /* TODO: Implement finish logic */ },
modifier = Modifier.fillMaxWidth()
) {
Text("Selesai")
}
}
}
}
}
@Composable
fun IndicatorItem(indicatorName: String, value: Float, onValueChange: (Float) -> Unit) {
val description = when (value.toInt()) {
0 -> "Tidak sama sekali"
1 -> "Beberapa hari"
2 -> "Lebih dari separuh hari"
3 -> "Hampir setiap hari"
else -> ""
}
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(indicatorName, style = MaterialTheme.typography.titleMedium)
Text(
text = value.toInt().toString(),
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
}
Spacer(modifier = Modifier.height(8.dp))
Slider(
value = value,
onValueChange = onValueChange,
valueRange = 0f..3f,
steps = 2,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = description,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CognitiveTestScreen(navController: NavController) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text("Pilih Tes Kognitif", style = MaterialTheme.typography.headlineSmall)
@Composable
fun TestCard(title: String, description: String, icon: ImageVector, route: String) {
Card(
onClick = { navController.navigate(route) },
modifier = Modifier.fillMaxWidth()
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(icon, contentDescription = null, modifier = Modifier.size(40.dp))
Spacer(Modifier.width(16.dp))
Column {
Text(title, style = MaterialTheme.typography.titleMedium)
Text(description, style = MaterialTheme.typography.bodySmall, color = Color.Gray)
}
}
}
}
TestCard(
title = "Tes Memori",
description = "Uji memori jangka pendek Anda",
icon = Icons.Default.Memory,
route = "memory_test"
)
TestCard(
title = "Tes Fokus",
description = "Uji kemampuan fokus & atensi",
icon = Icons.Default.CenterFocusStrong,
route = "focus_test"
)
TestCard(
title = "Tes Kecepatan Reaksi",
description = "Uji kecepatan reaksi visual Anda",
icon = Icons.Default.Speed,
route = "reaction_test"
)
}
}
// --- Cognitive Test Screens ---
data class MemoryCard(val id: Int, val icon: ImageVector, var isFaceUp: Boolean = false, var isMatched: Boolean = false)
enum class MemoryGameState { READY, PLAYING, FINISHED }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MemoryTestScreen(navController: NavController) {
val icons = listOf(
Icons.Default.Favorite, Icons.Default.Star, Icons.Default.ThumbUp,
Icons.Default.Spa, Icons.Default.Cloud, Icons.Default.Anchor
)
var cards by remember { mutableStateOf(createShuffledCards(icons)) }
var selectedCards by remember { mutableStateOf(listOf<MemoryCard>()) }
var moves by remember { mutableIntStateOf(0) }
var bestScore by remember { mutableStateOf<Int?>(null) }
var gameState by remember { mutableStateOf(MemoryGameState.READY) }
LaunchedEffect(cards.all { it.isMatched }) {
if (cards.all { it.isMatched } && gameState == MemoryGameState.PLAYING) {
gameState = MemoryGameState.FINISHED
if (bestScore == null || moves < bestScore!!) {
bestScore = moves
}
}
}
LaunchedEffect(selectedCards) {
if (selectedCards.size == 2) {
val (first, second) = selectedCards
if (first.icon == second.icon) {
cards = cards.map { if (it.id == first.id || it.id == second.id) it.copy(isMatched = true) else it }
} else {
delay(1000)
cards = cards.map { if (it.id == first.id || it.id == second.id) it.copy(isFaceUp = false) else it }
}
selectedCards = listOf()
}
}
fun restartGame() {
moves = 0
cards = createShuffledCards(icons)
gameState = MemoryGameState.PLAYING
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Tes Memori") },
navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) {
Icon(Icons.Default.ArrowBack, contentDescription = "Kembali")
}
}
)
}
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
when (gameState) {
MemoryGameState.READY -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text("Tes Memori", style = MaterialTheme.typography.headlineMedium)
bestScore?.let {
Text("Skor Terbaik: $it gerakan", style = MaterialTheme.typography.titleMedium)
}
Text(
"Tes ini menguji memori jangka pendek Anda. Cocokkan semua kartu dengan jumlah gerakan sesedikit mungkin.",
textAlign = TextAlign.Center
)
Button(onClick = { gameState = MemoryGameState.PLAYING }) {
Text("Mulai")
}
}
}
MemoryGameState.PLAYING, MemoryGameState.FINISHED -> {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) {
Text("Gerakan: $moves", style = MaterialTheme.typography.bodyLarge)
bestScore?.let {
Text("Skor Terbaik: $it", style = MaterialTheme.typography.bodyLarge)
}
}
Spacer(modifier = Modifier.height(16.dp))
LazyVerticalGrid(
columns = GridCells.Fixed(3),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(cards) { card ->
MemoryCardView(card = card, onCardClicked = {
if (gameState == MemoryGameState.PLAYING && !card.isFaceUp && !card.isMatched && selectedCards.size < 2) {
cards = cards.map { if (it.id == card.id) it.copy(isFaceUp = true) else it }
selectedCards = selectedCards + card
if (selectedCards.size == 1) moves++
}
})
}
}
Spacer(modifier = Modifier.weight(1f))
if (gameState == MemoryGameState.FINISHED) {
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp)){
Text("Selesai!", style = MaterialTheme.typography.headlineMedium, color = MaterialTheme.colorScheme.primary)
Button(onClick = { restartGame() }) {
Icon(Icons.Default.Refresh, contentDescription = "Coba Lagi")
Spacer(modifier = Modifier.width(8.dp))
Text("Coba Lagi")
}
}
}
}
}
}
}
}
fun createShuffledCards(icons: List<ImageVector>): List<MemoryCard> {
return (icons + icons).mapIndexed { index, icon -> MemoryCard(id = index, icon = icon) }.shuffled()
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MemoryCardView(card: MemoryCard, onCardClicked: () -> Unit) {
Card(
onClick = onCardClicked,
modifier = Modifier.aspectRatio(1f),
enabled = !card.isMatched,
colors = CardDefaults.cardColors(
containerColor = if (card.isFaceUp || card.isMatched) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.primary
)
) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
if (card.isFaceUp || card.isMatched) {
Icon(card.icon, contentDescription = null, modifier = Modifier.size(40.dp))
}
}
}
}
enum class FocusGameState { READY, PLAYING, FINISHED }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FocusTestScreen(navController: NavController) {
var score by remember { mutableIntStateOf(0) }
var highScore by remember { mutableIntStateOf(0) }
val normalColor = MaterialTheme.colorScheme.onSurface
var gridItems by remember { mutableStateOf(generateFocusGrid(normalColor)) }
var gameState by remember { mutableStateOf(FocusGameState.READY) }
var selectedDuration by remember { mutableIntStateOf(15) }
var timeLeft by remember { mutableIntStateOf(selectedDuration) }
LaunchedEffect(gameState) {
if (gameState == FocusGameState.PLAYING) {
timeLeft = selectedDuration
while (timeLeft > 0) {
delay(1000)
timeLeft--
}
if (timeLeft == 0) gameState = FocusGameState.FINISHED
} else if (gameState == FocusGameState.FINISHED) {
if (score > highScore) {
highScore = score
}
}
}
fun newLevel() {
gridItems = generateFocusGrid(normalColor)
}
fun restartGame() {
score = 0
gameState = FocusGameState.READY
newLevel()
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Tes Fokus") },
navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) {
Icon(Icons.Default.ArrowBack, contentDescription = "Kembali")
}
}
)
}
) { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
when (gameState) {
FocusGameState.READY -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text("Tes Fokus", style = MaterialTheme.typography.headlineMedium)
if (highScore > 0) {
Text("Skor Tertinggi: $highScore", style = MaterialTheme.typography.titleMedium)
}
Text(
"Tes ini menguji kemampuan fokus dan atensi Anda untuk mengidentifikasi perbedaan visual dengan cepat.",
textAlign = TextAlign.Center
)
Text("Pilih durasi waktu:")
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
val durations = listOf(15, 30, 60, 120)
durations.forEach { duration ->
val isSelected = selectedDuration == duration
OutlinedButton(
onClick = { selectedDuration = duration },
colors = if (isSelected) ButtonDefaults.outlinedButtonColors(containerColor = MaterialTheme.colorScheme.primaryContainer) else ButtonDefaults.outlinedButtonColors()
) {
Text("${duration}s")
}
}
}
Button(onClick = { gameState = FocusGameState.PLAYING }) {
Text("Mulai")
}
}
}
FocusGameState.PLAYING -> {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically
) {
Text("Skor: $score", style = MaterialTheme.typography.bodyLarge)
Text("Waktu: $timeLeft", style = MaterialTheme.typography.bodyLarge)
}
Spacer(modifier = Modifier.height(16.dp))
LazyVerticalGrid(
columns = GridCells.Fixed(5),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(gridItems.indices.toList()) { index ->
val item = gridItems[index]
Icon(
imageVector = item.icon,
contentDescription = null,
modifier = Modifier
.size(40.dp)
.rotate(item.rotation)
.clickable {
if (item.isDistractor) {
score++
newLevel()
} else {
if (score > 0) score--
}
},
tint = item.color
)
}
}
}
FocusGameState.FINISHED -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text("Waktu Habis!", style = MaterialTheme.typography.headlineMedium)
Text("Skor Akhir: $score", style = MaterialTheme.typography.bodyLarge)
Text("Skor Tertinggi: $highScore", style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold)
Button(onClick = { restartGame() }) {
Text("Coba Lagi")
}
}
}
}
}
}
}
data class FocusItem(val icon: ImageVector, val color: Color, val rotation: Float, val isDistractor: Boolean)
private fun generateFocusGrid(normalColor: Color): List<FocusItem> {
val gridSize = 25
val normalIcon = Icons.Default.Circle
val distractorIndex = Random.nextInt(gridSize)
val distractorType = Random.nextInt(3)
val distractor: FocusItem
val items = MutableList(gridSize) { FocusItem(normalIcon, normalColor, 0f, false) }
when (distractorType) {
0 -> { // Different Icon
distractor = FocusItem(Icons.Default.Star, normalColor, 0f, true)
}
1 -> { // Different Color
distractor = FocusItem(normalIcon, Color.Red, 0f, true)
}
else -> { // Different Rotation
distractor = FocusItem(normalIcon, normalColor, 90f, true)
// Use an icon that shows rotation
items.replaceAll { it.copy(icon = Icons.Default.Navigation) }
}
}
items[distractorIndex] = distractor
return items
}
enum class ReactionGameState { READY, WAITING, ACTION, FINISHED }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReactionSpeedTestScreen(navController: NavController) {
var state by remember { mutableStateOf(ReactionGameState.READY) }
var startTime by remember { mutableLongStateOf(0L) }
var reactionTime by remember { mutableLongStateOf(0L) }
var bestTime by remember { mutableStateOf<Long?>(null) }
val backgroundColor by animateColorAsState(
targetValue = when (state) {
ReactionGameState.WAITING -> Color.Red.copy(alpha = 0.8f)
ReactionGameState.ACTION -> Color.Green.copy(alpha = 0.8f)
else -> MaterialTheme.colorScheme.surface
},
animationSpec = tween(300),
label = "ReactionBackgroundColor"
)
val onScreenClick = {
when (state) {
ReactionGameState.WAITING -> {
reactionTime = -1 // Too soon
state = ReactionGameState.FINISHED
}
ReactionGameState.ACTION -> {
val newReactionTime = System.currentTimeMillis() - startTime
reactionTime = newReactionTime
if (bestTime == null || newReactionTime < bestTime!!) {
bestTime = newReactionTime
}
state = ReactionGameState.FINISHED
}
else -> { /* Clicks handled by buttons in READY and FINISHED states */ }
}
}
LaunchedEffect(state) {
if (state == ReactionGameState.WAITING) {
delay(Random.nextLong(1500, 5500))
if (state == ReactionGameState.WAITING) { // Ensure state hasn't changed
startTime = System.currentTimeMillis()
state = ReactionGameState.ACTION
}
}
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Tes Kecepatan Reaksi") },
navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) {
Icon(Icons.Default.ArrowBack, contentDescription = "Kembali")
}
}
)
}
) { innerPadding ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.background(backgroundColor)
.clickable(
enabled = state == ReactionGameState.WAITING || state == ReactionGameState.ACTION,
onClick = onScreenClick
),
contentAlignment = Alignment.Center
) {
when (state) {
ReactionGameState.READY -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(16.dp)
) {
Text("Tes Kecepatan Reaksi", style = MaterialTheme.typography.headlineMedium, color = MaterialTheme.colorScheme.onSurface)
bestTime?.let {
Text("Waktu Terbaik: $it ms", style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface)
}
Text(
"Tes ini mengukur kecepatan reaksi visual Anda. Tunggu layar berubah menjadi hijau, lalu tekan secepat mungkin.",
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.onSurface
)
Button(onClick = { state = ReactionGameState.WAITING }) {
Text("Mulai")
}
}
}
ReactionGameState.WAITING -> {
Text("Tunggu sampai hijau...", fontSize = 24.sp, color = Color.White, fontWeight = FontWeight.Bold)
}
ReactionGameState.ACTION -> {
Text("Tekan Sekarang!", fontSize = 24.sp, color = Color.White, fontWeight = FontWeight.Bold)
}
ReactionGameState.FINISHED -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.padding(16.dp)
) {
val resultText = if (reactionTime == -1L) "Terlalu Cepat!" else "${reactionTime} ms"
Text(resultText, fontSize = 48.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface)
bestTime?.let {
Text("Waktu Terbaik: $it ms", fontSize = 20.sp, color = MaterialTheme.colorScheme.onSurface)
}
Button(onClick = { state = ReactionGameState.READY }) {
Text("Coba Lagi")
}
}
}
}
}
}
}
@Composable
fun HistoryScreen() {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
Text("Riwayat & Grafik", style = MaterialTheme.typography.headlineSmall)
}
item {
GraphCard(title = "Tren Mood Mingguan")
}
item {
GraphCard(title = "Perkembangan Skor Depresi")
}
item {
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Insight AI", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(8.dp))
Text("AI menemukan pola bahwa mood Anda cenderung menurun di akhir pekan.", style = MaterialTheme.typography.bodyMedium)
}
}
}
}
}
@Composable
fun GraphCard(title: String) {
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(8.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(Color.LightGray.copy(alpha = 0.5f))
) {
Text("Area Grafik", modifier = Modifier.align(Alignment.Center), color = Color.Gray)
}
}
}
}