diff --git a/app/src/main/java/com/example/notesai/MainActivity.kt b/app/src/main/java/com/example/notesai/MainActivity.kt index 21fca69..97c54a4 100644 --- a/app/src/main/java/com/example/notesai/MainActivity.kt +++ b/app/src/main/java/com/example/notesai/MainActivity.kt @@ -1,10 +1,11 @@ package com.example.notesai - import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.* +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape @@ -19,6 +20,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import java.util.UUID import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import com.example.notesai.data.local.DataStoreManager import com.example.notesai.presentation.components.DrawerMenu @@ -37,20 +40,64 @@ import com.example.notesai.data.model.Category import com.example.notesai.util.updateWhere import kotlinx.coroutines.delay +// File: MainActivity.kt (Bagian Theme Setup) +// Ganti MaterialTheme di setContent dengan ini: + +import com.example.notesai.util.Constants + class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme( colorScheme = darkColorScheme( - primary = Color(0xFF6366F1), - secondary = Color(0xFFA855F7), - background = Color(0xFF0F172A), - surface = Color(0xFF1E293B), + // Primary colors + primary = Constants.AppColors.Primary, onPrimary = Color.White, + primaryContainer = Constants.AppColors.PrimaryContainer, + onPrimaryContainer = Color.White, + + // Secondary colors + secondary = Constants.AppColors.Secondary, onSecondary = Color.White, - onBackground = Color(0xFFE2E8F0), - onSurface = Color(0xFFE2E8F0) + secondaryContainer = Constants.AppColors.SecondaryVariant, + onSecondaryContainer = Color.White, + + // Background colors + background = Constants.AppColors.Background, + onBackground = Constants.AppColors.OnBackground, + + // Surface colors + surface = Constants.AppColors.Surface, + onSurface = Constants.AppColors.OnSurface, + surfaceVariant = Constants.AppColors.SurfaceVariant, + onSurfaceVariant = Constants.AppColors.OnSurfaceVariant, + + // Error colors + error = Constants.AppColors.Error, + onError = Color.White, + + // Other + outline = Constants.AppColors.Border, + outlineVariant = Constants.AppColors.Divider + ), + typography = Typography( + // Improve typography for better readability + displayLarge = MaterialTheme.typography.displayLarge.copy( + fontWeight = FontWeight.Bold + ), + headlineLarge = MaterialTheme.typography.headlineLarge.copy( + fontWeight = FontWeight.Bold + ), + titleLarge = MaterialTheme.typography.titleLarge.copy( + fontWeight = FontWeight.SemiBold + ), + bodyLarge = MaterialTheme.typography.bodyLarge.copy( + lineHeight = 24.sp + ), + bodyMedium = MaterialTheme.typography.bodyMedium.copy( + lineHeight = 20.sp + ) ) ) { Surface( @@ -161,7 +208,12 @@ fun NotesApp() { floatingActionButton = { AnimatedVisibility( visible = currentScreen == "main" && !showFullScreenNote, - enter = scaleIn() + fadeIn(), + enter = scaleIn( + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessLow + ) + ) + fadeIn(), exit = scaleOut() + fadeOut() ) { FloatingActionButton( @@ -173,20 +225,18 @@ fun NotesApp() { showCategoryDialog = true } }, - containerColor = Color.Transparent, - modifier = Modifier - .shadow(8.dp, CircleShape) - .background( - brush = Brush.linearGradient( - colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7)) - ), - shape = CircleShape - ) + containerColor = Constants.AppColors.Primary, + contentColor = Color.White, + elevation = FloatingActionButtonDefaults.elevation( + defaultElevation = 8.dp, + pressedElevation = 12.dp + ), + modifier = Modifier.size(64.dp) ) { Icon( Icons.Default.Add, contentDescription = if (selectedCategory != null) "Tambah Catatan" else "Tambah Kategori", - tint = Color.White + modifier = Modifier.size(28.dp) ) } } diff --git a/app/src/main/java/com/example/notesai/presentation/components/DrawerMenu.kt b/app/src/main/java/com/example/notesai/presentation/components/DrawerMenu.kt index 533d620..04eafd2 100644 --- a/app/src/main/java/com/example/notesai/presentation/components/DrawerMenu.kt +++ b/app/src/main/java/com/example/notesai/presentation/components/DrawerMenu.kt @@ -1,43 +1,29 @@ package com.example.notesai.presentation.components +import androidx.compose.animation.* +import androidx.compose.animation.core.* 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.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -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.statusBarsPadding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Archive -import androidx.compose.material.icons.filled.Create -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.Home -import androidx.compose.material.icons.filled.Star -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.Divider -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.* +import androidx.compose.material3.* +import androidx.compose.runtime.* 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.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.notesai.util.Constants @Composable fun DrawerMenu( @@ -45,133 +31,233 @@ fun DrawerMenu( onDismiss: () -> Unit, onItemClick: (String) -> Unit ) { + // Backdrop with blur effect Box( modifier = Modifier .fillMaxSize() - .statusBarsPadding() // Padding untuk status bar - .background(Color.Black.copy(alpha = 0.5f)) + .background(Constants.AppColors.Overlay) .clickable( onClick = onDismiss, indication = null, interactionSource = remember { MutableInteractionSource() } ) ) { - Card( + // Drawer Content + Surface( modifier = Modifier .fillMaxHeight() - .width(250.dp) + .width(280.dp) .align(Alignment.CenterStart) .clickable( onClick = {}, indication = null, interactionSource = remember { MutableInteractionSource() } ), - shape = RoundedCornerShape(topEnd = 0.dp, bottomEnd = 0.dp), - colors = CardDefaults.cardColors( - containerColor = Color(0xFF1E293B) - ), - elevation = CardDefaults.cardElevation(defaultElevation = 16.dp) + color = Constants.AppColors.Surface, + shadowElevation = Constants.Elevation.ExtraLarge.dp ) { - Column(modifier = Modifier.fillMaxSize()) { - // Header Drawer dengan tombol close + Column( + modifier = Modifier.fillMaxSize() + ) { + // Header - Minimalist Box( modifier = Modifier .fillMaxWidth() .background( brush = Brush.verticalGradient( - colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7)) + colors = listOf( + Constants.AppColors.Primary.copy(alpha = 0.15f), + Color.Transparent + ) ) ) - .padding(24.dp) + .padding(Constants.Spacing.ExtraLarge.dp) ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Column { + Column { + // App Icon with subtle background + Box( + modifier = Modifier + .size(56.dp) + .background( + color = Constants.AppColors.Primary.copy(alpha = 0.2f), + shape = RoundedCornerShape(Constants.Radius.Medium.dp) + ), + contentAlignment = Alignment.Center + ) { Icon( Icons.Default.Create, contentDescription = null, - tint = Color.White, - modifier = Modifier.size(36.dp) - ) - Spacer(modifier = Modifier.height(12.dp)) - Text( - "AI Notes", - style = MaterialTheme.typography.headlineMedium, - color = Color.White, - fontWeight = FontWeight.Bold - ) - Text( - "Smart & Modern", - style = MaterialTheme.typography.bodyMedium, - color = Color.White.copy(0.8f) + tint = Constants.AppColors.Primary, + modifier = Modifier.size(32.dp) ) } -// // Tombol Close -// IconButton( -// onClick = onDismiss, -// modifier = Modifier -// .size(40.dp) -// .background( -// Color.White.copy(alpha = 0.2f), -// shape = CircleShape -// ) -// ) { -// Icon( -// Icons.Default.Close, -// contentDescription = "Tutup Menu", -// tint = Color.White, -// modifier = Modifier.size(24.dp) -// ) -// } + Spacer(modifier = Modifier.height(Constants.Spacing.Medium.dp)) + + Text( + Constants.APP_NAME, + style = MaterialTheme.typography.headlineMedium, + color = Constants.AppColors.OnBackground, + fontWeight = FontWeight.Bold, + fontSize = 24.sp + ) + + Spacer(modifier = Modifier.height(Constants.Spacing.ExtraSmall.dp)) + + Text( + "Smart Note Taking", + style = MaterialTheme.typography.bodyMedium, + color = Constants.AppColors.OnSurfaceVariant, + fontSize = 14.sp + ) } } - Spacer(modifier = Modifier.height(16.dp)) + Spacer(modifier = Modifier.height(Constants.Spacing.Large.dp)) // Menu Items - MenuItem( - icon = Icons.Default.Home, + DrawerMenuItem( + icon = if (currentScreen == "main") Icons.Filled.Home else Icons.Outlined.Home, text = "Beranda", - isSelected = currentScreen == "main" - ) { onItemClick("main") } + isSelected = currentScreen == "main", + onClick = { onItemClick("main") } + ) - MenuItem( - icon = Icons.Default.Star, + DrawerMenuItem( + icon = if (currentScreen == "starred") Icons.Filled.Star else Icons.Outlined.StarBorder, text = "Berbintang", - isSelected = currentScreen == "starred" - ) { onItemClick("starred") } + isSelected = currentScreen == "starred", + onClick = { onItemClick("starred") } + ) - MenuItem( - icon = Icons.Default.Archive, + DrawerMenuItem( + icon = if (currentScreen == "archive") Icons.Filled.Archive else Icons.Outlined.Archive, text = "Arsip", - isSelected = currentScreen == "archive" - ) { onItemClick("archive") } + isSelected = currentScreen == "archive", + onClick = { onItemClick("archive") } + ) - MenuItem( - icon = Icons.Default.Delete, + DrawerMenuItem( + icon = if (currentScreen == "trash") Icons.Filled.Delete else Icons.Outlined.Delete, text = "Sampah", - isSelected = currentScreen == "trash" - ) { onItemClick("trash") } + isSelected = currentScreen == "trash", + onClick = { onItemClick("trash") } + ) Spacer(modifier = Modifier.weight(1f)) - // Footer - Divider( - color = Color.White.copy(alpha = 0.1f), - modifier = Modifier.padding(horizontal = 16.dp) + // Footer - Version info + HorizontalDivider( + color = Constants.AppColors.Divider, + modifier = Modifier.padding(horizontal = Constants.Spacing.Medium.dp) ) - Text( - text = "Version 1.0.0", - style = MaterialTheme.typography.bodySmall, - color = Color.White.copy(alpha = 0.5f), - modifier = Modifier.padding(16.dp) - ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(Constants.Spacing.Medium.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + "Version ${Constants.APP_VERSION}", + style = MaterialTheme.typography.bodySmall, + color = Constants.AppColors.OnSurfaceTertiary, + fontSize = 12.sp + ) + + // Powered by badge + Surface( + color = Constants.AppColors.Primary.copy(alpha = 0.1f), + shape = RoundedCornerShape(Constants.Radius.Small.dp) + ) { + Row( + modifier = Modifier.padding(horizontal = Constants.Spacing.Small.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + Icons.Default.AutoAwesome, + contentDescription = null, + tint = Constants.AppColors.Primary, + modifier = Modifier.size(12.dp) + ) + Text( + "AI", + color = Constants.AppColors.Primary, + fontSize = 11.sp, + fontWeight = FontWeight.Bold + ) + } + } + } } } } +} + +@Composable +private fun DrawerMenuItem( + icon: ImageVector, + text: String, + isSelected: Boolean, + onClick: () -> Unit +) { + // Scale animation + val scale by animateFloatAsState( + targetValue = if (isSelected) 1.02f else 1f, + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessMedium + ), + label = "scale" + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .scale(scale) + .clip(RoundedCornerShape(Constants.Radius.Medium.dp)) + .clickable(onClick = onClick) + .background( + color = if (isSelected) + Constants.AppColors.Primary.copy(alpha = 0.1f) + else + Color.Transparent + ) + .padding(horizontal = Constants.Spacing.Medium.dp, vertical = Constants.Spacing.Medium.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Icon with background + Box( + modifier = Modifier + .size(40.dp) + .background( + color = if (isSelected) + Constants.AppColors.Primary.copy(alpha = 0.2f) + else + Constants.AppColors.SurfaceVariant, + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + Icon( + icon, + contentDescription = null, + tint = if (isSelected) Constants.AppColors.Primary else Constants.AppColors.OnSurfaceVariant, + modifier = Modifier.size(20.dp) + ) + } + + Spacer(modifier = Modifier.width(Constants.Spacing.Medium.dp)) + + // Text + Text( + text, + style = MaterialTheme.typography.bodyLarge, + color = if (isSelected) Constants.AppColors.Primary else Constants.AppColors.OnSurface, + fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal, + fontSize = 15.sp + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/notesai/presentation/components/ModernBottomBar.kt b/app/src/main/java/com/example/notesai/presentation/components/ModernBottomBar.kt index d36f248..d160fd8 100644 --- a/app/src/main/java/com/example/notesai/presentation/components/ModernBottomBar.kt +++ b/app/src/main/java/com/example/notesai/presentation/components/ModernBottomBar.kt @@ -1,31 +1,32 @@ package com.example.notesai.presentation.components +import androidx.compose.animation.* +import androidx.compose.animation.core.* import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -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.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AutoAwesome import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Star -import androidx.compose.material3.BottomAppBar -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.material.icons.outlined.AutoAwesome +import androidx.compose.material.icons.outlined.Home +import androidx.compose.material.icons.outlined.StarBorder +import androidx.compose.material3.* +import androidx.compose.runtime.* 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.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.notesai.util.Constants @Composable fun ModernBottomBar( @@ -33,70 +34,125 @@ fun ModernBottomBar( onHomeClick: () -> Unit, onAIClick: () -> Unit ) { - BottomAppBar( - containerColor = Color.Transparent, + // Floating Bottom Bar with Glassmorphism + Box( modifier = Modifier - .shadow(8.dp, RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp)) - .background( - brush = Brush.verticalGradient( - colors = listOf( - Color(0xFF1E293B).copy(0.95f), - Color(0xFF334155).copy(0.95f) - ) - ), - shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp) - ) + .fillMaxWidth() + .padding(horizontal = 16.dp) ) { - Row( + Surface( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp), - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically + .shadow( + elevation = Constants.Elevation.Large.dp, + shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp) + ), + color = Constants.AppColors.SurfaceElevated, + shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp) ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, + Row( modifier = Modifier - .weight(1f) - .clickable(onClick = onHomeClick) - .padding(vertical = 8.dp) + .fillMaxWidth() + .padding(vertical = 8.dp, horizontal = 24.dp), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically ) { - Icon( - Icons.Default.Home, - contentDescription = "Home", - tint = if (currentScreen == "main") Color(0xFF6366F1) else Color(0xFF94A3B8), - modifier = Modifier.size(24.dp) + // Home Button + BottomBarItem( + selected = currentScreen == "main", + onClick = onHomeClick, + icon = if (currentScreen == "main") Icons.Filled.Home else Icons.Outlined.Home, + label = "Beranda" ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - "Home", - color = if (currentScreen == "main") Color(0xFF6366F1) else Color(0xFF94A3B8), - style = MaterialTheme.typography.bodySmall, - fontWeight = if (currentScreen == "main") FontWeight.Bold else FontWeight.Normal - ) - } - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .weight(1f) - .clickable(onClick = onAIClick) - .padding(vertical = 8.dp) - ) { - Icon( - Icons.Default.Star, - contentDescription = "AI Helper", - tint = if (currentScreen == "ai") Color(0xFFFBBF24) else Color(0xFF94A3B8), - modifier = Modifier.size(24.dp) - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - "AI Helper", - color = if (currentScreen == "ai") Color(0xFFFBBF24) else Color(0xFF94A3B8), - style = MaterialTheme.typography.bodySmall, - fontWeight = if (currentScreen == "ai") FontWeight.Bold else FontWeight.Normal + // AI Button + BottomBarItem( + selected = currentScreen == "ai", + onClick = onAIClick, + icon = if (currentScreen == "ai") Icons.Filled.AutoAwesome else Icons.Outlined.AutoAwesome, + label = "AI Helper" ) } } } +} + +@Composable +private fun BottomBarItem( + selected: Boolean, + onClick: () -> Unit, + icon: androidx.compose.ui.graphics.vector.ImageVector, + label: String +) { + // Scale animation + val scale by animateFloatAsState( + targetValue = if (selected) 1.1f else 1f, + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessLow + ), + label = "scale" + ) + + // Color animation + val iconColor by animateColorAsState( + targetValue = if (selected) Constants.AppColors.Primary else Constants.AppColors.OnSurfaceVariant, + animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM), + label = "color" + ) + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .clickable( + onClick = onClick, + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) + .padding(horizontal = 24.dp, vertical = 8.dp) + ) { + // Icon with background indicator + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(48.dp) + .scale(scale) + ) { + // Background indicator + if (selected) { + Box( + modifier = Modifier + .size(40.dp) + .background( + color = Constants.AppColors.Primary.copy(alpha = 0.15f), + shape = CircleShape + ) + ) + } + + Icon( + icon, + contentDescription = label, + tint = iconColor, + modifier = Modifier.size(24.dp) + ) + } + + // Label with fade animation + AnimatedVisibility( + visible = selected, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically() + ) { + Spacer(modifier = Modifier.height(4.dp)) + Text( + label, + color = Constants.AppColors.Primary, + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Bold, + fontSize = 12.sp + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/notesai/presentation/components/ModernTopBar.kt b/app/src/main/java/com/example/notesai/presentation/components/ModernTopBar.kt index 5d7cd99..253aa9a 100644 --- a/app/src/main/java/com/example/notesai/presentation/components/ModernTopBar.kt +++ b/app/src/main/java/com/example/notesai/presentation/components/ModernTopBar.kt @@ -1,42 +1,25 @@ package com.example.notesai.presentation.components +import androidx.compose.animation.* +import androidx.compose.animation.core.* import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -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.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.filled.Star -import androidx.compose.material3.BottomAppBar -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.example.notesai.util.Constants @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -50,58 +33,116 @@ fun ModernTopBar( onSearchQueryChange: (String) -> Unit, showSearch: Boolean ) { - TopAppBar( - title = { - if (showSearch) { - TextField( - value = searchQuery, - onValueChange = onSearchQueryChange, - placeholder = { Text("Cari catatan...", color = Color.White.copy(0.6f)) }, - colors = TextFieldDefaults.colors( - focusedContainerColor = Color.Transparent, - unfocusedContainerColor = Color.Transparent, - focusedTextColor = Color.White, - unfocusedTextColor = Color.White, - cursorColor = Color.White, - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent - ), - modifier = Modifier.fillMaxWidth() - ) - } else { - Text( - title, - fontWeight = FontWeight.Bold, - fontSize = 22.sp - ) - } + // Smooth transition for search bar + AnimatedContent( + targetState = showSearch, + transitionSpec = { + fadeIn(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM)) togetherWith + fadeOut(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM)) }, - navigationIcon = { - IconButton(onClick = if (showBackButton) onBackClick else onMenuClick) { - Icon( - if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu, - contentDescription = null, - tint = Color.White - ) + label = "topbar" + ) { isSearching -> + if (isSearching) { + // Search Mode - Minimalist + Surface( + modifier = Modifier + .fillMaxWidth() + .shadow(Constants.Elevation.Small.dp), + color = Constants.AppColors.Surface + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = Constants.Spacing.Medium.dp, vertical = Constants.Spacing.Small.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Search TextField + TextField( + value = searchQuery, + onValueChange = onSearchQueryChange, + placeholder = { + Text( + "Cari catatan atau kategori...", + color = Constants.AppColors.OnSurfaceVariant, + fontSize = 15.sp + ) + }, + colors = TextFieldDefaults.colors( + focusedContainerColor = Constants.AppColors.SurfaceVariant, + unfocusedContainerColor = Constants.AppColors.SurfaceVariant, + focusedTextColor = Constants.AppColors.OnBackground, + unfocusedTextColor = Constants.AppColors.OnSurface, + cursorColor = Constants.AppColors.Primary, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ), + modifier = Modifier + .weight(1f) + .heightIn(min = 48.dp), + shape = RoundedCornerShape(Constants.Radius.Medium.dp), + singleLine = true, + textStyle = LocalTextStyle.current.copy(fontSize = 15.sp) + ) + + Spacer(modifier = Modifier.width(Constants.Spacing.Small.dp)) + + // Close Search Button + IconButton( + onClick = { + onSearchQueryChange("") + onSearchClick() + } + ) { + Icon( + Icons.Default.Close, + contentDescription = "Close Search", + tint = Constants.AppColors.OnSurfaceVariant + ) + } + } } - }, - actions = { - IconButton(onClick = onSearchClick) { - Icon( - if (showSearch) Icons.Default.Close else Icons.Default.Search, - contentDescription = "Search", - tint = Color.White - ) + } else { + // Normal Mode - Minimalist + Surface( + modifier = Modifier + .fillMaxWidth() + .shadow(Constants.Elevation.Small.dp), + color = Constants.AppColors.Surface + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = Constants.Spacing.Small.dp, vertical = Constants.Spacing.Small.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Back/Menu Button + IconButton(onClick = if (showBackButton) onBackClick else onMenuClick) { + Icon( + if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu, + contentDescription = if (showBackButton) "Back" else "Menu", + tint = Constants.AppColors.OnSurface + ) + } + + // Title + Text( + title, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + color = Constants.AppColors.OnBackground, + modifier = Modifier.weight(1f) + ) + + // Search Button + IconButton(onClick = onSearchClick) { + Icon( + Icons.Default.Search, + contentDescription = "Search", + tint = Constants.AppColors.OnSurfaceVariant + ) + } + } } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = Color.Transparent - ), - modifier = Modifier - .background( - brush = Brush.horizontalGradient( - colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7)) - ) - ) - ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/notesai/presentation/screens/main/MainScreen.kt b/app/src/main/java/com/example/notesai/presentation/screens/main/MainScreen.kt index 88ded4f..26b5260 100644 --- a/app/src/main/java/com/example/notesai/presentation/screens/main/MainScreen.kt +++ b/app/src/main/java/com/example/notesai/presentation/screens/main/MainScreen.kt @@ -1,4 +1,3 @@ -// File: presentation/screens/main/MainScreen.kt package com.example.notesai.presentation.screens.main import androidx.compose.foundation.layout.* @@ -28,7 +27,7 @@ fun MainScreen( onNoteClick: (Note) -> Unit, onPinToggle: (Note) -> Unit, onCategoryDelete: (Category) -> Unit, - onCategoryEdit: (Category, String, Long, Long) -> Unit // Parameter baru + onCategoryEdit: (Category, String, Long, Long) -> Unit ) { Column(modifier = Modifier.fillMaxSize()) { if (selectedCategory == null) { @@ -58,7 +57,12 @@ fun MainScreen( } else { LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Fixed(2), - contentPadding = PaddingValues(16.dp), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = 16.dp, + bottom = 100.dp // Extra space untuk floating bottom bar + ), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalItemSpacing = 12.dp, modifier = Modifier.fillMaxSize() @@ -102,7 +106,12 @@ fun MainScreen( } else { LazyVerticalStaggeredGrid( columns = StaggeredGridCells.Fixed(2), - contentPadding = PaddingValues(16.dp), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = 16.dp, + bottom = 100.dp // Extra space untuk floating bottom bar + ), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalItemSpacing = 12.dp, modifier = Modifier.fillMaxSize() diff --git a/app/src/main/java/com/example/notesai/util/Constants.kt b/app/src/main/java/com/example/notesai/util/Constants.kt index 4575ccb..0416fec 100644 --- a/app/src/main/java/com/example/notesai/util/Constants.kt +++ b/app/src/main/java/com/example/notesai/util/Constants.kt @@ -1,11 +1,10 @@ -// File: util/Constants.kt package com.example.notesai.util import androidx.compose.ui.graphics.Color object Constants { // App Info - const val APP_NAME = "AI Notes" + const val APP_NAME = "NotesAI" const val APP_VERSION = "1.0.0" // DataStore @@ -17,37 +16,88 @@ object Constants { const val MAX_CHAT_PREVIEW_LINES = 2 const val GRID_COLUMNS = 2 - // Gradients - val GRADIENT_PRESETS = listOf( - Pair(0xFF6366F1L, 0xFFA855F7L), - Pair(0xFFEC4899L, 0xFFF59E0BL), - Pair(0xFF8B5CF6L, 0xFFEC4899L), - Pair(0xFF06B6D4L, 0xFF3B82F6L), - Pair(0xFF10B981L, 0xFF059669L), - Pair(0xFFF59E0BL, 0xFFEF4444L), - Pair(0xFF6366F1L, 0xFF8B5CF6L), - Pair(0xFFEF4444L, 0xFFDC2626L) - ) - - // Colors + // NEW MINIMALIST COLOR PALETTE object AppColors { - val Primary = Color(0xFF6366F1) - val Secondary = Color(0xFFA855F7) - val Background = Color(0xFF0F172A) - val Surface = Color(0xFF1E293B) - val SurfaceVariant = Color(0xFF334155) - val OnBackground = Color(0xFFE2E8F0) - val OnSurface = Color(0xFFE2E8F0) - val Success = Color(0xFF10B981) - val Error = Color(0xFFEF4444) - val Warning = Color(0xFFFBBF24) - val TextSecondary = Color(0xFF94A3B8) - val TextTertiary = Color(0xFF64748B) - val Divider = Color(0xFF334155) + // Backgrounds - Neutral Dark + val Background = Color(0xFF0A0A0A) // Almost black + val Surface = Color(0xFF141414) // Dark gray + val SurfaceVariant = Color(0xFF1E1E1E) // Lighter dark gray + val SurfaceElevated = Color(0xFF252525) // Elevated surface + + // Primary Accent - Subtle Blue (minimalist) + val Primary = Color(0xFF3B82F6) // Modern blue + val PrimaryVariant = Color(0xFF60A5FA) // Light blue + val PrimaryContainer = Color(0xFF1E3A8A) // Dark blue container + + // Secondary Accent - Minimal use + val Secondary = Color(0xFF8B5CF6) // Subtle purple + val SecondaryVariant = Color(0xFFA78BFA) // Light purple + + // Text Colors - High contrast for readability + val OnBackground = Color(0xFFFFFFFF) // Pure white + val OnSurface = Color(0xFFE5E5E5) // Off white + val OnSurfaceVariant = Color(0xFF9CA3AF) // Gray text + val OnSurfaceTertiary = Color(0xFF6B7280) // Muted gray + + // Functional Colors + val Success = Color(0xFF10B981) // Green + val Error = Color(0xFFEF4444) // Red + val Warning = Color(0xFFFBBF24) // Amber + val Info = Color(0xFF3B82F6) // Blue + + // Border & Dividers - Subtle + val Border = Color(0xFF2A2A2A) // Subtle border + val Divider = Color(0xFF1F1F1F) // Very subtle divider + + // Overlay & Shadows + val Overlay = Color(0xFF000000).copy(alpha = 0.5f) + val Shadow = Color(0xFF000000).copy(alpha = 0.3f) + + // Category Colors - Minimalist Palette (subtle & muted) + val CategoryColors = listOf( + Pair(0xFF3B82F6L, 0xFF60A5FAL), // Blue gradient + Pair(0xFF8B5CF6L, 0xFFA78BFAL), // Purple gradient + Pair(0xFF10B981L, 0xFF34D399L), // Green gradient + Pair(0xFFF59E0BL, 0xFFFBBF24L), // Amber gradient + Pair(0xFFEF4444L, 0xFFF87171L), // Red gradient + Pair(0xFF06B6D4L, 0xFF22D3EEL), // Cyan gradient + Pair(0xFFEC4899L, 0xFFF472B6L), // Pink gradient + Pair(0xFF6366F1L, 0xFF818CF8L) // Indigo gradient + ) } - // Animation - const val ANIMATION_DURATION = 300 + // Animation Durations + const val ANIMATION_DURATION_SHORT = 150 + const val ANIMATION_DURATION_MEDIUM = 300 + const val ANIMATION_DURATION_LONG = 500 const val FADE_IN_DURATION = 200 const val FADE_OUT_DURATION = 200 + + // Spacing System (8dp grid) + object Spacing { + const val ExtraSmall = 4 + const val Small = 8 + const val Medium = 16 + const val Large = 24 + const val ExtraLarge = 32 + const val XXLarge = 48 + } + + // Corner Radius + object Radius { + const val Small = 8 + const val Medium = 12 + const val Large = 16 + const val ExtraLarge = 20 + const val Round = 999 + } + + // Elevation + object Elevation { + const val None = 0 + const val Small = 2 + const val Medium = 4 + const val Large = 8 + const val ExtraLarge = 16 + } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f222f63..ed1e84f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,9 @@ appcompat = "1.6.1" material = "1.10.0" activity = "1.8.0" constraintlayout = "2.1.4" +uiText = "1.10.0" +material3 = "1.4.0" +animationCore = "1.10.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -19,6 +22,9 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version material = { group = "com.google.android.material", name = "material", version.ref = "material" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +androidx-ui-text = { group = "androidx.compose.ui", name = "ui-text", version.ref = "uiText" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } +androidx-animation-core = { group = "androidx.compose.animation", name = "animation-core", version.ref = "animationCore" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }